itemType, contentType, and renderType value across all sites, what each one fetches or displays, and the parameters each acceptsThree separate settings, easy to conflate: the container dropped onto a page (containerType: Stack, Carousel, Grid, or ButtonGroup) picks the layout shell. Each item inside that container has its own itemType, set directly on the item (not inside the Template JSON) — this is what kind of slot the item is (a button, a hero card, a contact row, or a template-driven List — see §2). Only when an item is meant to fetch real data does it also carry a Template JSON string, whose contentType says what to fetch and renderType says how each fetched row should look. This doc covers all three, organized item-first since that's the order an admin actually picks them in.
Every item starts with an itemType set directly on the item — this is not part of the Template JSON. Most itemType values (Button, ButtonCard, HeroCard, CardText, …) are simple, static visual slots: title/image/link are typed in directly and there's no Template at all (template: null).
An item becomes templated when it also carries a Template JSON string — either to fetch a single piece of live data (the signed-in pilot's own profile, their LEC link) onto an otherwise-static-looking slot, or to expand into a whole list of rows the admin doesn't know in advance (documents matching a category, a committee's roster, podcast episodes). For that second case, the convention used throughout every real site's content is to set the item's own itemType explicitly to List — a placeholder marking "this slot will be filled in dynamically" — and let the Template JSON's contentType and other parameters do the actual work of building it:
itemType: "List" (on the item) — the standard marker for "this item expands into N rows built from the Template." Used on the great majority of templated items in real site content.contentType (inside Template) — selects which data source the app queries.renderType (inside Template) — selects which visual card component renders each result row.itemType, contentType, and renderType are all free-text strings, not fixed picklists enforced by the app. There is no validation today that rejects a typo or an incompatible pairing — an unrecognized contentType silently drops the item (the rest of the page still renders fine); an unrecognized renderType throws internally but is caught and the item is skipped. Nothing tells you it was skipped. The dropdown-driven admin UI should therefore only ever offer the exact values listed in §2–§4 below — never free text — since a mistyped value fails silently rather than with an error message.
Set directly on the item, sibling to (not inside) the Template. Values seen in real, live site content:
itemType | What it is | Required fields (if static) |
|---|---|---|
List | Placeholder for a template-driven list — see §1. The item's own display fields are ignored; the Template's contentType/renderType decide everything about the resolved rows. | — |
Button | Plain text button. The most common item type in every site. | ctaText, ctaLink. Optionally carries a Template of just {"contentType":"ButtonCard","variant":"alpa"} or "alpa-diagonal" to render the branded stripe style instead of plain. |
ButtonCard | Square/rectangular tile with an icon — used in Grid containers for quick-link menus. | headerText, ctaLink; optional image (icon URL). |
CardSmall | Small compact card (icon/image + short title) — also the renderType most single-row content resolves to (e.g. LecLink, Committee). | title, image, ctaLink (or driven entirely by Template). |
HeroCard | Large, prominent card with image + description — used standalone (static) or as the renderType for fetched content like documents/podcasts. | title, image, link. (Legacy fields blurb/icon still work as fallbacks for description/image but should not be used in new content.) |
PilotCard | The personalized pilot greeting card. Always paired with a Template (contentType: "PilotCard") since it needs the signed-in pilot's session. | Template required — see §3. |
ContactCard | A single static contact row (name/role/phone/email). | rosterTitleToken/rosterNameToken/etc. typography tokens; otherwise driven by Template when fetched via ContactItem. |
MultiCommitteeRoster | Renders the combined roster for several committees at once: for each committee, in the order listed, a heading with the committee's name, then its members, officers first. | A Template with contentType: "RosterItem" and a rosterCodes parameter — a comma-separated list of committee codes, e.g. "UALLEC00097,UALLEC00107" (see §3 and §6). A top-level rosterCodes field on the item is not read. |
CardText | Text-only card, no image. | label (eyebrow), title, description, link. |
PromoBanner | Full-width promotional banner. | title, description, image, link. |
ToggleSwitch | An on/off toggle row. | title. |
SearchSecondaryButton | Secondary search-entry button. | ctaText, ctaLink. |
Expander | Collapsible/expandable section. | title, description (body when expanded). |
TabGroup | A set of tabs. | title (tab label). |
EmergencyButton | High-visibility emergency-contact button (red background). | ctaText, ctaLink (usually a tel: link). |
SingleButton2 | Alternate single-button style. | ctaText, ctaLink. |
ThreeUpButton | Row of exactly three icon+label buttons. | threeUpButtons array of {icon, label, link}, exactly 3 entries. |
SliderImages | A carousel-within-an-item image slider. | sliderTitle, sliderItems array of {imageUrl, eyebrow, title, blurb, link}. |
ButtonGroup containers only keep Button items — any other itemType placed inside a ButtonGroup container is silently filtered out client-side.
contentType | What it fetches | Produces | Status |
|---|---|---|---|
PilotCard | No data fetch — builds a personalized greeting card for the signed-in pilot (name, rank, contract link) from the device's own login session. | 1 row | Live |
LecLink | A single link row to the signed-in pilot's own LEC (Local Executive Council) page, based on their MEC/LEC/base airport. | 1 row | Live |
DocumentItem | Documents from the document library, filtered by category or grouping. | N rows (itemType: "List") | Live — the highest-volume contentType in real content |
Committee | The MEC's list of committees. | N rows | Live |
MecHotelDirectory | The signed-in member's MEC crew-hotel directory — airports and their hotels, as the backend returns them for that member's MEC (security-trimmed per MEC). | One flat, ordered list: a search field, then for each airport a header followed by that airport's hotel rows | New — live on one UAL page, ual-committees-hotel-list ("Hotel Directory"), reached from a "HOTEL LIST TEST" button on ual-committees-hotel |
ContactItem | The volunteer roster (officers/members) for one specific committee. | N rows (itemType: "List") | Live — heavily used, second-highest volume |
RosterItem | The volunteer rosters of several committees at once, one headed section per committee, in the order the codes are listed. Every live multi-committee roster attaches it to an item whose itemType is MultiCommitteeRoster. | For each committee: a heading with its name, then its member rows | Live — 20 roster items in the fallback content (UAL 12, DAL 8) |
PodcastEpisode | Episodes from an RSS podcast feed. | N rows (itemType: "List") | Live |
ButtonCard | No data fetch — this is a styling selector, not a fetch. Attached to a plain Button item to render it as the branded stripe button (see variant, §5) instead of a plain text button. | 0 rows — decorates the existing item in place | Live |
HeroCard | No data fetch — same styling-only pattern as ButtonCard, attached to an item whose itemType is already HeroCard. Used for a static hero card whose title/image/action are typed straight into the Template's parameters rather than the item's own fields, e.g. an "Access {mec} documents" tile with action: "/mec". | 0 rows — decorates the existing item in place | Live |
| (omitted) | Leaving contentType off (Template null) entirely is valid — the item is fully static, using whatever title/description/image/link were typed directly on it. | 1 row (the item itself) | Live |
RosterItem is what makes an item a multi-committee roster. The committee list lives in the Template's rosterCodes parameter, and the item resolves like any other templated item, so hidden, visibleMecs, hiddenMecs and hiddenUserTypes apply to it and isFavoritable is honored. The app picks the roster by this content type, not by the item's itemType. An item-level rosterCodes field (with no Template) is not read: such an item shows nobody.
Each contentType adapter has a sensible default renderType it falls back to if left blank, but it can be overridden. Observed valid values, by how the card looks:
renderType | Visual result | Typically paired with |
|---|---|---|
PilotCard | The personalized pilot greeting card (has its own dedicated layout; variant: "alpa" selects the ALPA-branded sub-style). | PilotCard (always) |
CardSmall | Small, compact card — icon/image plus a short title. The most heavily used renderType overall. | LecLink, Committee |
HeroCard | Large, prominent card with a bigger image and description — used for featured/lead content. Second most common renderType. | DocumentItem (usual default), PodcastEpisode, hand-authored static hero items |
ContactCard | A person/contact row (name, role, phone, email) — the renderType used for essentially every ContactItem resolution. | ContactItem (always, in practice) |
CardText | Text-only card, no image. | hand-authored text cards |
ButtonCard | A simple tappable button-style card, no fetched data required. Also used as a value of contentType (see §3) when attached to a plain Button item — those two uses are independent. | hand-authored, or a Button item's contentType: "ButtonCard" styling template |
The following renderType values were not observed in any live mock content, but exist as their own itemType (§2) and can be reasonably expected to work as a renderType the same way: PromoBanner, Expander, TabGroup, ToggleSwitch, SearchSecondaryButton. Confirm with engineering before relying on one of these from a Template rather than a plain static item.
If unsure, leave renderType blank and let the contentType's adapter pick its default (LecLink → CardSmall, PilotCard → PilotCard, Committee → CardSmall, MecHotelDirectory → MecHotelRow). DocumentItem, ContactItem, and PodcastEpisode require renderType to be set explicitly — the item is silently dropped if it's left blank on those three.
These sit alongside contentType/renderType in the same JSON object and apply regardless of content type:
| Field | Valid values | Effect |
|---|---|---|
hidden | true / false | Hides the item entirely when true. |
visibleMecs | Comma-separated MEC codes, e.g. "UAL,DAL"; group tokens "USA"/"CAN" supported. | Allow-list — item shows only for members of the listed MEC(s). |
hiddenMecs | Comma-separated MEC codes. | Deny-list — item hidden for members of the listed MEC(s). |
hiddenUserTypes | Array of strings, e.g. ["staff","guest"]. | Hides the item from the listed user types. |
isFavoritable | true / false | Shows/hides the favorite (star) toggle on the card. |
variant | Content-type specific: "alpa" (branded), "alpa-diagonal" (branded diagonal-stripe — only confirmed on ButtonCard-styled Button items). | Selects a visual sub-style within a renderType. Plain passthrough, no {token} substitution. |
clampTitle | true / false (default true) | A HeroCard placed directly on the page (its own title, image and link) only; a hero that a List builds from documents or podcast episodes ignores it. false shows the card's whole title instead of cutting it off after two lines; leaving it out, or true, keeps the two-line limit. Write false or "false"; on/off only, not a line count. Used for Home's "From the President" card. A hero with no Template gets this whole template: {"renderType":"HeroCard","contentType":"HeroCard","clampTitle":false}. Keep renderType and contentType: a template with no contentType, or with nothing else the app reads (a misspelled key such as ClampTitle, or a value such as 0 or "no"), removes the card from the page. With renderType present, a mistake only leaves the title cut at two lines. Plain passthrough, no {token} substitution. |
title, eyebrow, description, image, ctaLink, link | Plain strings; support {token} placeholders (e.g. {mec}, {lec}, {airport}) filled in per-viewer. | Overrides the card's own text/image/link when the content type doesn't otherwise fill it in, or for hand-authored (no contentType) cards. |
action | Left null in almost every real item. When set, seen as: a plain in-app route ("/mec", "/home-mec" — on a static HeroCard-styled item, §3), or a route template with an {Id} placeholder ("/mec/committee/{Id}" — Committee's per-row web-fallback link, §6). Supports {token} substitution the same as ctaLink/link. | Sets the item's nav target (mirrors ctaLink/link — see §6's per-contentType notes for which name a given contentType expects). |
containerTitle | Plain string; supports {token}, e.g. "{mec} MEC". | Sets the heading on the container this item lives in (not the item itself). |
viewAllLink | Not populated on any item's Template in real content — see the note below. | Sets the container's "View All" link (only takes effect when the container's own viewAllLink field, below, is empty). |
When the four fields above (hidden, visibleMecs, hiddenMecs, hiddenUserTypes) hide every item in a container from a member, the whole container disappears for that member: no heading, no "View All" link, and for a carousel no "Off the Radar!" card. Home's "My MEC" carousel works this way for a GGN pilot and for Staff and Guest accounts. A hidden item is always hidden, but it never takes the rest of its container with it: a container with any item the fields did not hide always shows. That includes a document list that found nothing (a carousel shows its "Off the Radar!" card, a grid or stack its heading with nothing under it) and one that failed to load (it shows as loading and retries).
In practice, set viewAllLink on the container itself, not inside an item's Template. The Template-level viewAllLink key is real and does get lifted onto the container when present, but no item in any real site's content actually uses it — every site instead sets the container's own top-level viewAllLink field directly. Real values observed there, by shape:
| Shape | Example |
|---|---|
| In-app page-key shorthand | "{ual-media}", "{ual-lec-005-updates}", "{ala-communications}", "{aca-publications}" |
Compound shorthand with a nested {mec}/{lec} token | "{{mec}-events}", "{{mec}-lec-{lec}-updates}", "{{mec}-publications}" |
| Full in-app route | "/dynamic-feed-orchestrator?page={alpa-updates}", "/mec" |
| Absolute external URL (usually a CMS search/results page) | "https://ual.alpa.org/RESOURCES/Updates-Reports-Documents#cf-item-category=Committees,SOAR", "https://dal.alpa.org/Search#sortCriteria=%40itemdate%20descending&cf-item-category=Communications" |
One malformed value was found in real content: site.DAL.json has a viewAllLink of "{dal-publications-contract2026" — missing its closing brace. Don't copy that pattern; always close a page-key shorthand with }.
| Field | Valid values | Notes |
|---|---|---|
filterType | "Category" (by far the most common) or "Grouping" (seen once in live UAL content: ual-updates-committeeupdates uses filterType: "Grouping" with filterValue: "MEC Committee Updates" and the grouping field left null — the group name goes in filterValue) | Required — picks which document filter to apply. |
filterValue | Category or grouping name, e.g. "MEC Updates and Weekly", "Hotel Updates", "FAQ", "Pilot Compensation" | Required. Free text — must match an actual document category name exactly. |
filterValues | A JSON array of category (or grouping) names, e.g. ["Communications", "Hotel Updates"] | Optional — one carousel or list from several categories (or several groupings, matching filterType). Their documents are merged: a document in two of them shows once, and docType and searchFilter apply to the merged set, so limit=3 is the 3 newest across all of them, not 3 from each. Without a date sort, categories show in the order listed. Each name must match a category exactly; one with no documents is skipped. Keep filterValue as well, set to one of the names: an app version from before this field reads only filterValue, so it shows that one category instead of an empty carousel. Must be an array — a single string such as "Communications, Hotel Updates" is ignored and only filterValue is used. One filterType per item: categories and groupings can't be mixed. For View All, give the page the container's viewAllLink opens the same filterValues (and filterValue) with a larger limit, so it lists the same combined set at length. |
category | null in every real item, with no exceptions — no site has ever populated this field. | Optional — intended to further narrow a Grouping query. Leave omitted/null; there is no known-good example value to copy. |
parentFilterType / parentFilterValue | "MEC" or "LEC" / the code, e.g. "ual", "UAL", or a numeric LEC code like "005", "063" | Scopes to one airline/local group. Case of the value itself is inconsistent across existing content (both "UAL" and "ual" seen) — match whatever the source system uses. |
scope | null in every real item, with no exceptions — no site has ever populated this field. | Present in the parameter bag but has no known-good example value; leave omitted/null. |
grouping | null in every real item — never populated for DocumentItem specifically (compare ContactItem's grouping below, which does see real use). | Present in the parameter bag alongside filterType: "Grouping"; still never populated, even in the one live Grouping item (see filterType above). |
searchFilter | "sort_by=date&order=desc&limit=3" | Use exactly this query-string form. The colon-delimited form ("by:date-direction:desc-top:3") was the pattern in older site files, but the app's parser only understands the query-string form, so any remaining colon-delimited values are silently no-ops. The current UAL site file (2026-09) uses the query-string form on every item that sets searchFilter (e.g. limit=5, limit=10, limit=25). New/edited content should use the query-string form. |
action | null in every real DocumentItem — always left out since the document viewer route is set automatically per row. | Optional per-row tap override; no known-good non-null example exists for this contentType specifically (compare Committee's action below). |
| Field | Valid values | Notes |
|---|---|---|
action | Route template with {Id}, e.g. "/mec/committee/{Id}" | Per-row web-fallback tap destination. |
searchFilter | "sort_by=name&order=asc" | Defaults to alphabetical by name if omitted/unrecognized. |
featuredCommittees | JSON object mapping a committee code to an icon URL, e.g. {"UALHOTL00001":"https://.../hotel-icon.svg"} | Featured committees are pinned in the given order ahead of the rest. |
featuredCommitteePages | JSON object mapping the same committee code to an in-app CMS page key, e.g. {"UALHOTL00001":"ual-committees-hotel"} | Overrides the destination page for a featured committee when tapped in-app (action above is the web fallback). |
In real content, a Committee template is typically attached directly to a CardSmall item (not an itemType: "List" placeholder) — either convention works, since the app never reads the placeholder item's own itemType once a factory resolves it.
| Field | Valid values | Notes |
|---|---|---|
filterType | "RosterCode" (most content), "LecReps" (AB#2863) or "MecReps" (AB#2840) | Required. "RosterCode" resolves one committee's own volunteer roster; "LecReps" resolves the signed-in pilot's own LEC reps instead (getlecreps) — a fallback source for LECs with no AMPS committee group to point at; "MecReps" resolves the signed-in pilot's MEC officers (getmecreps). Read Officer and rep sections below before using "MecReps". |
filterValue | Committee/roster code, e.g. "UALHOTL00001", "ACAMEC00007" | Required for filterType: "RosterCode". Ignored for filterType: "LecReps" and "MecReps" — omit it or leave any value, it has no effect in those modes. |
parentFilterType / parentFilterValue | Same as DocumentItem | Optional scoping. Not read in LecReps or MecReps mode. |
grouping | "Officers" is the only non-null value ever seen, anywhere, across every site. Otherwise null. | Accepted but not currently applied — the full roster returns regardless of this value, so "Officers" doesn't actually filter anything today even where it's set. Not read at all in LecReps or MecReps mode. |
scope, category, searchFilter, action | null in every real item — no exceptions found. | Present in the parameter bag; leave omitted/null, there is no known-good example value. |
filterType: "LecReps" and "MecReps" rows show the favorite heart when the rep record
carries the person's id (memberId, the same id a committee volunteer carries), and favorite as that
person, so an officer, the same person's rep record and their committee rows are one favorite and, in Search, one
card. A rep record without that id has no heart.
Row order is automatic. Every ContactItem roster (both filterTypes), every
RosterItem roster, the LEC roster page and Search list officers first, by one shared title list in the app
(a volunteer's title, a representative's office or office code): any Chair title (any subject chair, such as
Safety Chair, included) and the National President and Vice President offices, then Vice Chair, then Secretary
or Secretary-Treasurer, then Treasurer, then Executive Administrator, then Captain Representative, then First Officer
Representative (a plural such as Captain Representatives ranks the same), then everyone else. Titles match whatever
their capitals, and a space, a hyphen or any other dash (an en or em dash pasted from a document), a slash or a
backslash (including their full-width, division and fraction forms), or an invisible character pasted with the text
(a zero-width space, a soft hyphen, a byte-order mark) between or around words is the same, so Vice-Chair ranks as
Vice Chair and Secretary/Treasurer as Secretary-Treasurer.
Beyond that list, a title whose last word is Chair (or Chairman, Chairwoman or Chairperson) ranks as Chair, or as
Vice Chair with Vice before that word, a note in brackets at the end such as (Interim) ignored; a Past, Former or
Emeritus chair and Chair-Elect are not ranked.
The same reading decides when two titles from one committee are the same line on a person's card. Within the same rank, a
person's sortorder (from the roster data, not the Template) comes first, then legal last name, then legal
first name, for committee members, reps and MEC officers alike (a nickname never moves a person).
There is no Template field for ordering; a title that should rank but doesn't needs adding to that list in the app.
One card per person, on the roster, in Search and in Favorites (AB#2840). The people a
ContactItem or RosterItem section lists are what members find in the app's Search and see
in Favorites, so how a section is set up shows there too:
LecReps
section the council's own committee (e.g. "Delta Council 66"). Someone with one role keeps the roster's card, with
the committee named on a line above it. Any of their titles, committees or names finds the same card.hidden, visibleMecs,
hiddenMecs and hiddenUserTypes hide only the roles that section lists: a member who
can't see the section doesn't see those lines, and can't find the person by them, but still sees the person
through any section they can see.isFavoritable: false on a section removes the heart from
that section's rows; in Search the person keeps a heart only if another section the member can see them through
allows it.MecReps, LecReps)What they list (AB#2840). A ContactItem section with filterType: "MecReps"
lists the signed-in pilot's MEC officers, straight from the officer data (getmecreps);
filterType: "LecReps" lists their own council's LEC reps (getlecreps). Both need
renderType: "ContactCard", like every ContactItem, and honor isFavoritable and the
visibility fields.
LecReps section lists the reps of the pilot's own
council only; the app drops every other council's reps before anything is stored on the device.LecReps section, and the older reps screen shows the same seat under the officer's office.Moving an officer or rep section onto MecReps / LecReps. Today these
people are mostly authored as committee rosters (e.g. UAL's "MEC Officers", UALMADM00003, and the council
"REPRESENTATIVES" sections), which rank as committee lines. To move one:
MecReps is
widely installed: an older build doesn't know the filter and shows the section empty.filterType (and drop its
filterValue) rather than adding a second section beside it. Two sections listing the same people show
them twice on the page.isFavoritable and roster typography tokens: the rows keep the section's look, and its place on the page
decides where its people sit in Search.| Field | Valid values | Notes |
|---|---|---|
rosterCodes | Comma-separated committee codes, e.g. "UALLEC00097,UALLEC00107" | Required. Sections appear in the order listed, not in the order the server returns committees. A repeated code (any letter case) shows once, at its first position; an unknown code, or a committee with no published members, is skipped. A committee whose name is blank in the data is headed by its code. |
renderType | "ContactCard" | Optional; defaults to ContactCard, which is what every live roster uses (none sets it). Applies to the member rows; the headings are always headings. |
isFavoritable | true / false | Optional, default true. Same meaning as on ContactItem: false hides the favorite hearts on every member row. |
hidden, visibleMecs, hiddenMecs, hiddenUserTypes | See §5 | Hide the whole roster from the members they name, like any templated item. |
| Typography tokens | rosterTitleToken, rosterNameToken, rosterEmailToken, rosterTelToken, set on the item, not in the Template | Style each member's role, name, email and phone. Every member row inherits them from the item. |
If the committee data can't be loaded, or none of the codes has published members, the roster shows nothing; the rest of the page is unaffected.
| Field | Valid values | Notes |
|---|---|---|
filterType | "FeedUrl" (only value seen) | Required. |
filterValue | RSS feed URL, e.g. "https://feed.podbean.com/.../feed.xml" | Required. |
searchFilter | e.g. limit/top segment | Feed is already newest-first; only a result-count limit has effect. Same query-string-vs-colon-form caveat as DocumentItem applies. |
| Field | Valid values | Notes |
|---|---|---|
parentFilterType | "MEC" | Always this value in real content. |
parentFilterValue | MEC code, e.g. "UAL", "ALA" | Required. |
filterType | "LEC" (only value seen) | — |
filterValue | "SELF" (only value seen) | Always resolves to the signed-in pilot's own LEC. |
image | URL/filename pattern with a {lec} token, e.g. ".../{lec}-card-sm.jpg" | Optional — overrides the default image, substituting the pilot's resolved LEC code. |
A one-line Template — it has no filter or scope parameters. Which airports and hotels appear is decided by the backend for the signed-in member's MEC, so there is nothing to configure per site beyond placing it.
| Field | Valid values | Notes |
|---|---|---|
contentType | "MecHotelDirectory" | Required — the only field the directory needs. |
renderType | "MecHotelRow" (what the live UAL page sets) or blank (defaults to MecHotelRow) | No other value is meaningful for this content type today; overriding it would send hotel rows to a card layout that does not know their fields. |
filterType, filterValue, parentFilterType, parentFilterValue, searchFilter | — | Not read. Do not set them expecting to narrow the list. |
hidden, visibleMecs, hiddenMecs, hiddenUserTypes | As in §5 | Optional, and apply to the item like any other. They control whether the directory section shows; the hotel data itself is already trimmed to the member's MEC by the backend. |
What it produces, and how it behaves. The Template resolves into one flat, ordered list — a search field, then for each airport (alphabetical by airport code; airports with no hotels are skipped) a header ("CODE · Airport Name") followed by that airport's hotel rows. Grouping is the item order, so the list must be rendered as-is.
Stack container. The search field is lifted out of the Stack into the screen's fixed area so it stays pinned while the list scrolls, and search reaches rows only inside a Stack. In other container shapes (Carousel, Grid, List) the list still renders but the field is not pinned and search will not filter the rows.emptyBehavior applies — set it to Message or Suppress deliberately rather than leaving an empty section./mec-hotel-detail) — the destination is set by the app, not by the Template.| Field | Valid values | Notes |
|---|---|---|
filterType | "MEC" (always) | — |
filterValue | "SELF" (always) | — |
ctaLink | Deep-link string, e.g. "/document-open?queryTagID=AppContractLink" | Route opened when the pilot taps "View Contract". |
variant | e.g. "alpa" | Optional visual sub-style. |
isFavoritable | true / false | Usually false for this card. |
Attached to a plain Button item, not a fetch-producing content type. Accepts variant ("alpa" or "alpa-diagonal"), plus the common visibility fields from §5 (hiddenUserTypes, hiddenMecs, visibleMecs) and filterType/filterValue/ctaLink when it also needs a per-pilot deep link.
Put it in a Stack container. In a Stack each committee's heading sits directly above that committee's members, as one list. In a Carousel or Grid the roster still resolves, but each heading and each member becomes its own slide or tile, so the sections no longer read as sections.
Set itemType: "MultiCommitteeRoster" and attach a Template with "contentType": "RosterItem" and the rosterCodes to show, in display order (parameters in the RosterItem table above). The item's own display fields are ignored; the four roster*Token typography tokens on the item apply to every member row. An item-level rosterCodes field is not read: an item that has only that field shows nobody.
When assembling a page, two independent choices happen at two different levels:
| Level | Setting | Valid values | Controls |
|---|---|---|---|
| Container (the thing dropped onto the page) | containerType | Stack, Carousel, Grid, ButtonGroup | The overall layout shell the items sit inside. Grid also takes an optional columns integer (defaults to 3 when omitted). |
| Item (each slot inside the container) | itemType | See §2 | What kind of slot the item is. |
| Item's Template (only for fetched content) | contentType / renderType | See §3 / §4 | What data fills that slot and which card style it renders as. |
In practice: an admin first picks the container (say, Carousel), then for each item inside it picks an itemType (say, List for fetched content), then a contentType (say, DocumentItem) and, if needed, overrides the renderType (say, HeroCard) plus that content type's filter parameters from §6. The container choice does not constrain which itemType/contentType/renderType values are valid — any content type can be placed in any container shape (with the one exception noted in §2: ButtonGroup keeps only Button items) — so the admin dropdown can treat the container and item choices independently.
Each example below shows the item's itemType alongside its Template JSON, exactly as they appear together on the item.
This is the exact shape of the live UAL ual-committees-hotel-list page: one Stack container (columns: 1) holding a single List item with this Template. The item on that page also carries roster*Token values (the same tokens the committee-roster items use); the directory's rows are built by the factory and do not read them, so they have no effect.