ProtoWiki components
ProtoWiki ships a small, deliberate set of components in src/components/.
They're all plain Vue 3 components — no registry, no special mounting, no
"prototype framework". Use them like any other Vue component.
This skill is the cross-cutting guide. Per-component depth lives in
references/:
references/wrappers.md — ChromeWrapper,
SpecialPageWrapper, PlainWrapper, MobileWrapper
references/chrome-primitives.md —
ChromeHeader, VectorChromeHeader, MinervaChromeHeader, ChromeFooter
references/app-chrome.md —
AppChromeWrapper, AppChromeHeader, AppBottomMenu
references/article.md — ArticleWrapper, ArticleRenderer,
ArticleLive, ArticleSnapshot, ArticleCustom, ArticleHeader
references/search.md
references/editors.md —
Visual editor prototyping outside ProtoWiki (fork Bárbara Martínez Calvo’s article template + suggestion-mode repos)
references/edit-suggestions.md —
Edit Check-style suggestion stream alongside your editing surface (payload
shape, side-by-side layout, SuggestionCard, publish interception)
references/dashboard.md — Dashboard, DashboardModule
references/attribution.md — AttributionCard, useAttributionSignals, off-wiki attribution fetch helpers
references/composition-recipes.md
The shape of the catalogue
| Component |
Concern |
Renders chrome? |
Renders columns? |
ChromeWrapper |
Wikipedia chrome (header + footer) around a slot |
Yes |
No |
AppChromeWrapper |
App chrome (header + bottom nav) around a slot |
Yes |
No |
SpecialPageWrapper |
Special-page shell — title row + optional help/actions + content |
No |
No (full-width) |
PlainWrapper |
Centred narrow column — no chrome (gallery / Component-style demos) |
No |
No |
MobileWrapper |
Phone-frame preview — full width below 480px; centred column + neutral Codex gutters when clamped |
No |
No |
ChromeHeader |
Skin-aware web header — Vector (desktop) or Minerva (mobile); wordmarks, search cluster, user tools |
n/a |
n/a |
VectorChromeHeader |
Force desktop Vector 2022 chrome — wordmarks, inline search, nav tools |
n/a |
n/a |
MinervaChromeHeader |
Force mobile Minerva bar — left / middle / right item arrays |
n/a |
n/a |
ChromeFooter |
Footer chrome (via ChromeWrapper): desktop Vector strip with optional mock last-edited + CC lines, or mobile Minerva well + optional strip |
n/a |
n/a |
AppChromeHeader |
App top bar — left / middle / right item arrays (via AppChromeWrapper) |
n/a |
n/a |
AppBottomMenu |
App bottom icon nav — home, saved, search, history, menu (via AppChromeWrapper) |
n/a |
n/a |
ArticleWrapper |
Reader outer <article>: ArticleHeader (web) or the apps' lead block (app) + default slot (main column — usually ArticleRenderer) — no v-html by itself |
No |
No |
ArticleRenderer |
Parser column (.article-content, .mw-parser-output, #default only); mobile section > h2 on mobile skin, in-app affordances with app — title chrome lives elsewhere |
No |
No |
ArticleLive |
ArticleWrapper + nested ArticleRenderer for REST page/html (+ cache); progress/errors in default slot before ArticleRenderer. Omit article → random article each load (source = 'random' | 'vital', langs); app → in-app screen with lead block |
No |
No |
ArticleSnapshot |
ArticleWrapper + ArticleRenderer (omitted until snapshot load succeeds or #default) + public/snapshots/ |
No |
No |
ArticleCustom |
ArticleWrapper + ArticleRenderer: #default is the parser body — no page/html, no snapshot file |
No |
No |
ArticleHeader |
Title row, tabs, read/edit/history, tools (used inside ArticleWrapper, web only) |
No |
No |
Search |
CdxTypeaheadSearch wired to opensearch (default in VectorChromeHeader) |
n/a |
n/a |
Dashboard |
Newcomer homepage grid — #banner, #mobile, #primary, #sidebar slots |
No |
Yes (desktop) |
DashboardModule |
Single module box — link card when to is set, static sidebar card otherwise |
No |
No |
AttributionCard |
Off-wiki attribution card (essential + trust signals + optional CTAs); variant full / compact / inline |
No |
No |
Defaults, props, and slots (shared contract)
Most regions follow the same pattern:
- Default — sensible markup/CSS in the component.
- Prop — tweak with the same base name as the paired slot where it fits (e.g.
title / #title, help / #help). Boolean toggles such as actions stay plain nouns — no show* prefix.
- Named slot
#x — replaces the default inner content for that region; when the slot is supplied, it wins over the prop-fed default.
Examples: title / #title / #header (SpecialPageWrapper cluster), help (boolean or #help), actions (boolean or #actions), username / #username, ChromeHeader navTools vs #nav.
The two ideas you need
1. Single concern, compose by nesting
Each layout shell does one thing. To get a full Wikipedia-shaped article
page, you nest:
<ChromeWrapper>
<ArticleLive article="Albert Einstein" />
</ChromeWrapper>
Two lines, top-down: chrome → article surface. ArticleLive and ArticleSnapshot each compose ArticleWrapper with an ArticleRenderer in its default slot (plus fetch or snapshot UI). ArticleCustom is the same ArticleRenderer slot without fetching — use for hand-authored markup (src/prototypes/template-article-custom/).
The app shell nests the same way — swap the chrome, add app to the article surface:
<AppChromeWrapper>
<ArticleLive app article="Albert Einstein" />
</AppChromeWrapper>
See protowiki-app-prototyping for the app side end to end.
2. Shared skin / theme on every themable component; lang / dir on layout shells + article surfaces
Every component in this list (ChromeWrapper,
SpecialPageWrapper, PlainWrapper, ArticleWrapper, ArticleRenderer,
ArticleLive, ArticleSnapshot, ArticleCustom, Search) accepts the same two theming props:
| Prop |
Type |
Effect |
skin |
'desktop' | 'mobile' |
Sets data-skin="…" on the root, locally re-skinning the subtree |
theme |
'light' | 'dark' |
Sets data-theme="…" on the root, locally re-theming the subtree |
The layout wrappers (ChromeWrapper, SpecialPageWrapper,
PlainWrapper), ArticleWrapper, ArticleRenderer, ArticleLive, ArticleSnapshot, ArticleCustom accept:
| Prop |
Type |
Effect |
lang |
string (BCP-47) |
Sets lang="…" on the component root |
dir |
'ltr' | 'rtl' |
Sets dir="…" on the component root — pass it explicitly; ProtoWiki does not infer it from lang |
Usually you set lang / dir once on ChromeWrapper (or <html>); use ArticleWrapper /
ArticleRenderer / ArticleLive / ArticleCustom props when you need language or direction on the article subtree only. Chrome primitives inherit lang / dir through the DOM and do not repeat these props.
When skin / theme props are omitted, components resolve effective
skin/theme from the nearest ChromeWrapper (Vue provide/inject for ArticleLive
/ ArticleSnapshot / ArticleCustom / SpecialPageWrapper), then from global boot state on
<html>. Roots set data-skin / data-theme from that resolution so [data-skin] CSS stays aligned with nested previews.
App prototypes are the exception. AppChromeWrapper provides theme but no
skin — app chrome has no Vector/Minerva concept — so an article surface with
app pins its own skin to mobile and you never pass skin inside an app
shell. The iOS / Android split is a separate axis (data-app-platform); see
protowiki-app-prototyping.
See protowiki-skins and
protowiki-theme for the full theming
model.
Imports
All components live at @/components/<Name>.vue:
import ChromeWrapper from '@/components/chrome/ChromeWrapper.vue'
import ChromeHeader from '@/components/chrome/ChromeHeader.vue'
import VectorChromeHeader from '@/components/chrome/VectorChromeHeader.vue'
import MinervaChromeHeader from '@/components/chrome/MinervaChromeHeader.vue'
import ChromeFooter from '@/components/chrome/ChromeFooter.vue'
import AppChromeWrapper from '@/components/app/AppChromeWrapper.vue'
import AppChromeHeader from '@/components/app/AppChromeHeader.vue'
import AppBottomMenu from '@/components/app/AppBottomMenu.vue'
import SpecialPageWrapper from '@/components/SpecialPageWrapper.vue'
import PlainWrapper from '@/components/PlainWrapper.vue'
import MobileWrapper from '@/components/MobileWrapper.vue'
import ArticleWrapper from '@/components/article/ArticleWrapper.vue'
import ArticleRenderer from '@/components/article/ArticleRenderer.vue'
import ArticleLive from '@/components/article/ArticleLive.vue'
import ArticleSnapshot from '@/components/article/ArticleSnapshot.vue'
import ArticleCustom from '@/components/article/ArticleCustom.vue'
import ArticleHeader from '@/components/article/ArticleHeader.vue'
import Search from '@/components/Search.vue'
import Dashboard from '@/components/dashboard/Dashboard.vue'
import DashboardModule from '@/components/dashboard/DashboardModule.vue'
The @/ prefix resolves to src/.
Quick props/slots overview
| Component |
Key props |
Notable slots |
ChromeWrapper |
lang?, dir?, skin?, theme?, lastEditedNotice?, username?, wordmarkSrc?, taglineSrc?, mobileWordmarkSrc?, navTools? (ChromeNavTool[], forwarded to default header) |
default, #header, #footer |
AppChromeWrapper |
lang?, dir?, theme?, showBottomMenu?, left?, middle?, right?, bottomNavItems? |
default, #header, #bottomMenu |
SpecialPageWrapper |
title?, help? (boolean), actions?, lang?, dir?, skin?, theme? |
default, #header, #title, #help, #actions |
PlainWrapper |
heading?, lang?, dir? |
default, #heading |
MobileWrapper |
maxWidth? (default 360px), lang?, dir? |
default |
ChromeHeader |
skin?, theme?, username?, wordmarkSrc?, taglineSrc?, mobileWordmarkSrc?, navTools?, left?, middle?, right? (mobile) |
#menu, #logo, #username, #nav |
VectorChromeHeader |
theme?, username?, wordmarkSrc?, taglineSrc?, navTools? |
#menu, #logo, #username, #nav |
MinervaChromeHeader |
theme?, left?, middle?, right?, wordmarkSrc?, mobileWordmarkSrc? |
— |
ChromeFooter |
skin?, theme?, lastEditedNotice?, username? |
default |
AppChromeHeader |
theme?, left?, middle?, right? |
— |
AppBottomMenu |
theme?, items? |
default, #item-{id} |
ArticleWrapper |
title?, header?, languagesCount?, lang, dir, skin, theme, app? / description? / leadImageUrl? (app lead block) |
default |
ArticleRenderer |
lang/dir/skin/theme, app? (pins skin to mobile) |
default — parser subtree ( ArticleLive / ArticleSnapshot use v-html here unless #default is forwarded ) |
ArticleLive |
Same ArticleWrapper chrome + article (page/html title; omit for random) + host + app? + random-mode source? ('random'|'vital') / langs? / vitalLevel? (source="vital"); emits parserReady |
default → ArticleRenderer (ArticleLive injects Cdx progress/errors before ArticleRenderer) |
ArticleSnapshot |
article (snapshot key + ArticleWrapper title) + chrome passthroughs (no host / header) + app? |
default → ArticleRenderer (ArticleSnapshot injects Cdx UI before ArticleRenderer) |
ArticleCustom |
Same ArticleWrapper chrome keys as manual composition (title, header, app?, …) — no article / host |
default → ArticleRenderer (your #default is the parser subtree) |
ArticleHeader |
title (required), languagesCount? (default 18), skin? |
#title, emits (languageSelect, languageSettingsClick, tab/action clicks) |
Search |
host?, placeholder?, limit?, skin?, theme? |
none |
Dashboard |
(none) |
#banner, #mobile, #primary, #sidebar |
DashboardModule |
title?, to?, cta?, subtle? |
default, #cta |
When to reach beyond this list
If you need a UI primitive (button, form field, dialog, toast, table,
checkbox, tabs, etc.), use a Codex component directly — see
codex-components. Don't add a new wrapper
for one prototype's needs; put the bespoke layout inside that prototype's
folder.
1---2name: protowiki-components3description: Catalog of every shipped component in src/components/ — the single-concern layout wrappers (ChromeWrapper, SpecialPageWrapper, PlainWrapper, MobileWrapper), web chrome primitives (ChromeHeader, VectorChromeHeader, MinervaChromeHeader, ChromeFooter), app chrome (AppChromeWrapper, AppChromeHeader, AppBottomMenu), Article surfaces (`ArticleWrapper` + `ArticleRenderer`, ArticleLive, ArticleSnapshot, ArticleCustom, ArticleHeader — all three surfaces take an `app` prop for in-app screens), dashboard layout (`Dashboard`, `DashboardModule`), attribution (`AttributionCard`, `useAttributionSignals`), and Search — including hand-authored article HTML in `ArticleRenderer`'s default slot (see `src/prototypes/template-article-custom/`) and newcomer homepage templates (`template-dashboard`, `template-homepage`). Use when picking a wrapper, composing a page, looking up props/slots/events for any ProtoWiki component, or asking "what components does ProtoWiki ship?".4---56# ProtoWiki components78ProtoWiki ships a small, deliberate set of components in `src/components/`.9They're all plain Vue 3 components — no registry, no special mounting, no10"prototype framework". Use them like any other Vue component.1112This skill is the cross-cutting guide. Per-component depth lives in13`references/`:1415- [`references/wrappers.md`](references/wrappers.md) — `ChromeWrapper`,16 `SpecialPageWrapper`, `PlainWrapper`, `MobileWrapper`17- [`references/chrome-primitives.md`](references/chrome-primitives.md) —18 `ChromeHeader`, `VectorChromeHeader`, `MinervaChromeHeader`, `ChromeFooter`19- [`references/app-chrome.md`](references/app-chrome.md) —20 `AppChromeWrapper`, `AppChromeHeader`, `AppBottomMenu`21- [`references/article.md`](references/article.md) — `ArticleWrapper`, `ArticleRenderer`,22 `ArticleLive`, `ArticleSnapshot`, `ArticleCustom`, `ArticleHeader`23- [`references/search.md`](references/search.md)24- [`references/editors.md`](references/editors.md) —25 Visual editor prototyping **outside** ProtoWiki (fork Bárbara Martínez Calvo’s article template + suggestion-mode repos)26- [`references/edit-suggestions.md`](references/edit-suggestions.md) —27 Edit Check-style suggestion stream alongside **your** editing surface (payload28 shape, side-by-side layout, `SuggestionCard`, publish interception)29- [`references/dashboard.md`](references/dashboard.md) — `Dashboard`, `DashboardModule`30- [`references/attribution.md`](references/attribution.md) — `AttributionCard`, `useAttributionSignals`, off-wiki attribution fetch helpers31- [`references/composition-recipes.md`](references/composition-recipes.md)3233## The shape of the catalogue3435| Component | Concern | Renders chrome? | Renders columns? |36| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ---------------- |37| `ChromeWrapper` | Wikipedia chrome (header + footer) around a slot | Yes | No |38| `AppChromeWrapper` | App chrome (header + bottom nav) around a slot | Yes | No |39| `SpecialPageWrapper` | Special-page shell — title row + optional help/actions + content | No | No (full-width) |40| `PlainWrapper` | Centred narrow column — no chrome (gallery / Component-style demos) | No | No |41| `MobileWrapper` | Phone-frame preview — full width below 480px; centred column + neutral Codex gutters when clamped | No | No |42| `ChromeHeader` | Skin-aware web header — Vector (desktop) or Minerva (mobile); wordmarks, search cluster, user tools | n/a | n/a |43| `VectorChromeHeader` | Force desktop Vector 2022 chrome — wordmarks, inline search, nav tools | n/a | n/a |44| `MinervaChromeHeader` | Force mobile Minerva bar — `left` / `middle` / `right` item arrays | n/a | n/a |45| `ChromeFooter` | Footer chrome (via ChromeWrapper): **desktop** Vector strip with optional mock last-edited + CC lines, or **mobile** Minerva well + optional strip | n/a | n/a |46| `AppChromeHeader` | App top bar — `left` / `middle` / `right` item arrays (via AppChromeWrapper) | n/a | n/a |47| `AppBottomMenu` | App bottom icon nav — home, saved, search, history, menu (via AppChromeWrapper) | n/a | n/a |48| `ArticleWrapper` | Reader outer **`<article>`**: **`ArticleHeader`** (web) or the apps' lead block (**`app`**) + **default slot** (**main column** — usually **`ArticleRenderer`**) — no **`v-html`** by itself | No | No |49| `ArticleRenderer` | Parser column (**`.article-content`**, **`.mw-parser-output`**, **`#default`** only); mobile **`section > h2`** on mobile skin, in-app affordances with **`app`** — title chrome lives elsewhere | No | No |50| `ArticleLive` | **`ArticleWrapper`** + nested **`ArticleRenderer`** for REST **`page/html`** (+ cache); progress/errors in **default slot** before **`ArticleRenderer`**. **Omit `article` → random article each load** (**`source`** = `'random'` \| `'vital'`, **`langs`**); **`app`** → in-app screen with lead block | No | No |51| `ArticleSnapshot` | **`ArticleWrapper`** + **`ArticleRenderer`** (omitted until snapshot load succeeds or **`#default`**) + **`public/snapshots/`** | No | No |52| `ArticleCustom` | **`ArticleWrapper`** + **`ArticleRenderer`**: **`#default`** is the parser body — no **`page/html`**, no snapshot file | No | No |53| `ArticleHeader` | Title row, tabs, read/edit/history, tools (used inside **`ArticleWrapper`**, web only) | No | No |54| `Search` | `CdxTypeaheadSearch` wired to opensearch (default in VectorChromeHeader) | n/a | n/a |55| `Dashboard` | Newcomer homepage grid — `#banner`, `#mobile`, `#primary`, `#sidebar` slots | No | Yes (desktop) |56| `DashboardModule` | Single module box — link card when `to` is set, static sidebar card otherwise | No | No |57| `AttributionCard` | Off-wiki attribution card (essential + trust signals + optional CTAs); `variant` full / compact / inline | No | No |5859## Defaults, props, and slots (shared contract)6061Most regions follow the same pattern:62631. **Default** — sensible markup/CSS in the component.642. **Prop** — tweak with the **same base name** as the paired slot where it fits (e.g. **`title`** / **`#title`**, **`help`** / **`#help`**). Boolean toggles such as **`actions`** stay plain nouns — no `show*` prefix.653. **Named slot `#x`** — replaces the default **inner** content for that region; when the slot is supplied, it wins over the prop-fed default.6667Examples: `title` / `#title` / `#header` (**`SpecialPageWrapper`** cluster), **`help`** (**`boolean`** or **`#help`**), **`actions`** (**`boolean`** or **`#actions`**), **`username`** / **`#username`**, `ChromeHeader` **`navTools`** vs **`#nav`**.6869## The two ideas you need7071### 1. Single concern, compose by nesting7273Each layout shell does **one** thing. To get a full Wikipedia-shaped article74page, you nest:7576```vue77<ChromeWrapper>78 <ArticleLive article="Albert Einstein" />79</ChromeWrapper>80```8182Two lines, top-down: chrome → article surface. **`ArticleLive`** and **`ArticleSnapshot`** each compose **`ArticleWrapper`** with an **`ArticleRenderer`** in its default slot (plus fetch or snapshot UI). **`ArticleCustom`** is the same **`ArticleRenderer`** slot without fetching — use for hand-authored markup (**`src/prototypes/template-article-custom/`**).8384The app shell nests the same way — swap the chrome, add **`app`** to the article surface:8586```vue87<AppChromeWrapper>88 <ArticleLive app article="Albert Einstein" />89</AppChromeWrapper>90```9192See [`protowiki-app-prototyping`](../protowiki-app-prototyping/SKILL.md) for the app side end to end.9394### 2. Shared `skin` / `theme` on every themable component; `lang` / `dir` on layout shells + article surfaces9596Every component in this list (`ChromeWrapper`,97`SpecialPageWrapper`, `PlainWrapper`, `ArticleWrapper`, `ArticleRenderer`,98`ArticleLive`, `ArticleSnapshot`, `ArticleCustom`, `Search`) accepts the same two theming props:99100| Prop | Type | Effect |101| ------- | ----------------------- | ----------------------------------------------------------------- |102| `skin` | `'desktop' \| 'mobile'` | Sets `data-skin="…"` on the root, locally re-skinning the subtree |103| `theme` | `'light' \| 'dark'` | Sets `data-theme="…"` on the root, locally re-theming the subtree |104105The **layout wrappers** (`ChromeWrapper`, `SpecialPageWrapper`,106`PlainWrapper`), **`ArticleWrapper`**, **`ArticleRenderer`**, **`ArticleLive`**, **`ArticleSnapshot`**, **`ArticleCustom`** accept:107108| Prop | Type | Effect |109| ------ | ----------------- | -------------------------------------------------------------------------------------------------- |110| `lang` | `string` (BCP-47) | Sets `lang="…"` on the component root |111| `dir` | `'ltr' \| 'rtl'` | Sets `dir="…"` on the component root — pass it explicitly; ProtoWiki does not infer it from `lang` |112113Usually you set `lang` / `dir` once on `ChromeWrapper` (or `<html>`); use **`ArticleWrapper`** /114**`ArticleRenderer`** / **`ArticleLive`** / **`ArticleCustom`** props when you need language or direction on the article subtree only. Chrome primitives inherit `lang` / `dir` through the DOM and do not repeat these props.115116When `skin` / `theme` props are omitted, components resolve **effective**117skin/theme from the nearest `ChromeWrapper` (Vue provide/inject for **`ArticleLive`**118/ **`ArticleSnapshot`** / **`ArticleCustom`** / **`SpecialPageWrapper`**), then from global boot state on119`<html>`. Roots set `data-skin` / `data-theme` from that resolution so `[data-skin]` CSS stays aligned with nested previews.120121**App prototypes are the exception.** `AppChromeWrapper` provides `theme` but no122`skin` — app chrome has no Vector/Minerva concept — so an article surface with123**`app`** pins its own skin to `mobile` and you never pass `skin` inside an app124shell. The iOS / Android split is a separate axis (`data-app-platform`); see125[`protowiki-app-prototyping`](../protowiki-app-prototyping/SKILL.md).126127See [`protowiki-skins`](../protowiki-skins/SKILL.md) and128[`protowiki-theme`](../protowiki-theme/SKILL.md) for the full theming129model.130131## Imports132133All components live at `@/components/<Name>.vue`:134135```ts136import ChromeWrapper from '@/components/chrome/ChromeWrapper.vue'137import ChromeHeader from '@/components/chrome/ChromeHeader.vue'138import VectorChromeHeader from '@/components/chrome/VectorChromeHeader.vue'139import MinervaChromeHeader from '@/components/chrome/MinervaChromeHeader.vue'140import ChromeFooter from '@/components/chrome/ChromeFooter.vue'141import AppChromeWrapper from '@/components/app/AppChromeWrapper.vue'142import AppChromeHeader from '@/components/app/AppChromeHeader.vue'143import AppBottomMenu from '@/components/app/AppBottomMenu.vue'144import SpecialPageWrapper from '@/components/SpecialPageWrapper.vue'145import PlainWrapper from '@/components/PlainWrapper.vue'146import MobileWrapper from '@/components/MobileWrapper.vue'147import ArticleWrapper from '@/components/article/ArticleWrapper.vue'148import ArticleRenderer from '@/components/article/ArticleRenderer.vue'149import ArticleLive from '@/components/article/ArticleLive.vue'150import ArticleSnapshot from '@/components/article/ArticleSnapshot.vue'151import ArticleCustom from '@/components/article/ArticleCustom.vue'152import ArticleHeader from '@/components/article/ArticleHeader.vue'153import Search from '@/components/Search.vue'154import Dashboard from '@/components/dashboard/Dashboard.vue'155import DashboardModule from '@/components/dashboard/DashboardModule.vue'156```157158The `@/` prefix resolves to `src/`.159160## Quick props/slots overview161162| Component | Key props | Notable slots |163| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |164| `ChromeWrapper` | `lang?`, `dir?`, `skin?`, `theme?`, **`lastEditedNotice?`**, **`username?`**, **`wordmarkSrc?`**, **`taglineSrc?`**, **`mobileWordmarkSrc?`**, **`navTools?`** (`ChromeNavTool[]`, forwarded to default header) | default, `#header`, `#footer` |165| `AppChromeWrapper` | `lang?`, `dir?`, `theme?`, **`showBottomMenu?`**, **`left?`**, **`middle?`**, **`right?`**, **`bottomNavItems?`** | default, `#header`, `#bottomMenu` |166| `SpecialPageWrapper` | `title?`, **`help?`** (**`boolean`**), **`actions?`**, `lang?`, `dir?`, `skin?`, `theme?` | default, **`#header`**, **`#title`**, `#help`, `#actions` |167| `PlainWrapper` | `heading?`, `lang?`, `dir?` | default, `#heading` |168| `MobileWrapper` | `maxWidth?` (default `360px`), `lang?`, `dir?` | default |169| `ChromeHeader` | `skin?`, `theme?`, **`username?`**, **`wordmarkSrc?`**, **`taglineSrc?`**, **`mobileWordmarkSrc?`**, **`navTools?`**, **`left?`**, **`middle?`**, **`right?`** (mobile) | `#menu`, `#logo`, `#username`, `#nav` |170| `VectorChromeHeader` | `theme?`, **`username?`**, **`wordmarkSrc?`**, **`taglineSrc?`**, **`navTools?`** | `#menu`, `#logo`, `#username`, `#nav` |171| `MinervaChromeHeader` | `theme?`, **`left?`**, **`middle?`**, **`right?`**, **`wordmarkSrc?`**, **`mobileWordmarkSrc?`** | — |172| `ChromeFooter` | `skin?`, `theme?`, **`lastEditedNotice?`**, **`username?`** | default |173| `AppChromeHeader` | `theme?`, **`left?`**, **`middle?`**, **`right?`** | — |174| `AppBottomMenu` | `theme?`, **`items?`** | default, `#item-{id}` |175| `ArticleWrapper` | **`title?`**, **`header?`**, **`languagesCount?`**, **`lang`**, **`dir`**, **`skin`**, **`theme`**, **`app?`** / **`description?`** / **`leadImageUrl?`** (app lead block) | **default** |176| `ArticleRenderer` | **`lang`/`dir`/`skin`/`theme`**, **`app?`** (pins `skin` to `mobile`) | **default** — parser subtree ( **`ArticleLive`** / **`ArticleSnapshot`** use **`v-html`** here unless **`#default`** is forwarded ) |177| `ArticleLive` | Same **`ArticleWrapper`** chrome **`+`** **`article`** (**`page/html`** title; **omit for random**) **`+`** **`host`** **`+`** **`app?`** **`+`** random-mode **`source?`** (`'random'`\|`'vital'`) / **`langs?`** / **`vitalLevel?`** (`source="vital"`); emits **`parserReady`** | **default** → **`ArticleRenderer`** (**`ArticleLive`** injects **`Cdx`** progress/errors before **`ArticleRenderer`**) |178| `ArticleSnapshot` | **`article`** (snapshot key **`+`** **`ArticleWrapper`** **`title`**) **`+`** chrome passthroughs (no **`host`** / **`header`**) **`+`** **`app?`** | **default** → **`ArticleRenderer`** (**`ArticleSnapshot`** injects **`Cdx`** UI before **`ArticleRenderer`**) |179| `ArticleCustom` | Same **`ArticleWrapper`** chrome keys as manual composition (**`title`**, **`header`**, **`app?`**, …) — **no `article` / `host`** | **default** → **`ArticleRenderer`** (your **`#default`** is the parser subtree) |180| `ArticleHeader` | **`title`** (required), **`languagesCount?`** (default 18), **`skin?`** | **`#title`**, emits (`languageSelect`, `languageSettingsClick`, tab/action clicks) |181| `Search` | `host?`, `placeholder?`, `limit?`, `skin?`, `theme?` | none |182| `Dashboard` | (none) | `#banner`, `#mobile`, `#primary`, `#sidebar` |183| `DashboardModule` | `title?`, `to?`, `cta?`, `subtle?` | default, `#cta` |184185## When to reach beyond this list186187If you need a UI primitive (button, form field, dialog, toast, table,188checkbox, tabs, etc.), use a Codex component directly — see189[`codex-components`](../codex-components/SKILL.md). Don't add a new wrapper190for one prototype's needs; put the bespoke layout inside that prototype's191folder.