07 - Component Library (Houses Module)
- 1.
HouseColorCard(list grid card) - 2.
HouseHeader(detail banner) - 3.
MemberTile(member row) - 4.
ColorPickerField(editor field) - 5.
HouseCountBadge(members count) - 6.
ConflictFieldError(shared, module-configured) - 7.
DeleteHouseDialog(confirm) - 8. Composition recipes
- 9. Component inventory vs. shared
Module-specific widgets on top of the shared library in 00-shared/03 (Component Library) and tokens in 00-shared/02 (Design Tokens). Shared primitives reused here (not redefined):
AppListPage,AppCard,AppListTile,AppButton,AppTextField,AppBottomSheet,AppDialog,AppMenu,AppFAB,AppSkeleton,AppEmptyState,AppOfflineBanner,AppSnackbar,AppPagination,AppChips,AppBadge,AppAvatar.
1. HouseColorCard (list grid card)
| Prop | Type | Default |
|---|---|---|
house | House | - |
onTap | VoidCallback? | - |
onEdit / onDelete | VoidCallback? | null (hidden when null) |
selectedColor | Color | resolved via HouseColorResolver (11 §3) |
- Layout: 16 dp swatch strip on top (height 56), name (titleMedium), code chip + motto 1-line ellipsis (bodySmall muted).
- States: default / pressed (elevation+
m-fast) / loading skeleton variant. - a11y: semantics label
"House <name>, code <code>"; never color-only. - Gesture: tap → detail;
AppMenu(ellipsis top-right, admin only).
2. HouseHeader (detail banner)
| Prop | Type |
|---|---|
house | House |
actions | List<Widget> (menu) |
- Full-width banner painted with resolved house color; dark scrim for contrast
(06 §2.5); name displayLarge white, code chip
onPrimary, motto italic. - Skeleton variant height 180.
- a11y: banner is decorative; name/code/motto are real text nodes.
3. MemberTile (member row)
| Prop | Type |
|---|---|
admissionNumber | String |
name (from joined student record, (planned) fuller payload) | String? |
onTap | VoidCallback? |
AppAvatarwith initials (deterministic hue from admission number - not house color, keeps house swatch meaningful), leading badgeAppBadge"member".- Row shows admissionNumber; name only when the students payload provides it
(today the list returns full student docs -
student.schema.ts:16-63- so name must be derived from the linked user, which the houses flow does not resolve; render admissionNumber-first(planned)name enrichment). - Empty state variant handled by parent (06 §2.3).
4. ColorPickerField (editor field)
| Prop | Type |
|---|---|
value | String? (hex #RRGGBB as stored, house.schema.ts:15-16) |
onChanged | ValueChanged<String?> |
presets | List<ColorToken> from 11 §3 |
- Renders 8 preset swatch buttons (selected ring) + custom hex
AppTextField(validates#RRGGBB). - Contrast hint: shows pass/warn against white text (00-shared/09).
- Semantics: each swatch
Semantics(button, label: "color <name>").
5. HouseCountBadge (members count)
| Prop | Type |
|---|---|
count | int? (null = unverified) |
unverified | bool |
- Verified (server count,
(planned)endpoint):AppBadge"<n> members". - Unverified (client join today):
"~<n> members"+ tooltip "server-side member count planned". Prevents presenting an estimated count as authoritative (06 §2.3).
6. ConflictFieldError (shared, module-configured)
- Reused from 00-shared/03; rendered under the code field with server message
verbatim:
House code "X" already exists.(houses.service.ts:19-21). - a11y: live-region announcement; focus moves to offending field.
7. DeleteHouseDialog (confirm)
- Extends
AppDialog: title "Delete house?", body lists consequences:- Soft delete (
houses.service.ts:51-54) - house disappears from lists. - Members keep
houseId- dangling refs (student.schema.ts:41-42); no server guard/cascade today(planned).
- Soft delete (
- Destructive action styled
error, confirm text "Delete". - a11y: focus trap, destructive-first reading order (00-shared/09).
8. Composition recipes
| Screen | Composition |
|---|---|
| House list (05 §1) | AppListPage + HouseColorCard grid + AppPagination + AppFAB |
| House detail (05 §2) | HouseHeader + members header (HouseCountBadge) + MemberTile list |
| House editor (05 §3) | AppBottomSheet/AppDialog + AppTextField x3 + ColorPickerField + AppButton + ConflictFieldError |
9. Component inventory vs. shared
| Widget | Owned by | Why not shared |
|---|---|---|
HouseColorCard | houses | house-specific data shape + swatch layout |
HouseHeader | houses | banner composition |
MemberTile | houses (student data rendered here) | cross-module display concern |
ColorPickerField | houses | free-string color semantics (server stores any string) |
HouseCountBadge | houses | verified/unverified duality |
DeleteHouseDialog | houses | delete semantics without server guard |