diff --git a/docs/proposals/mockups/wtc-rebuild/SPEC-wtc-rebuild.md b/docs/proposals/mockups/wtc-rebuild/SPEC-wtc-rebuild.md
index 4614e89d2..c7ae6786e 100644
--- a/docs/proposals/mockups/wtc-rebuild/SPEC-wtc-rebuild.md
+++ b/docs/proposals/mockups/wtc-rebuild/SPEC-wtc-rebuild.md
@@ -177,6 +177,17 @@ above rather than proposing anything new):**
its ActionGrid siblings now — hierarchy comes from position (its own row, above the grid)
and colour (`.primary`, orange) rather than from being a larger control.
+14. **A14: Exclusion-group chip width + group boundary (#742/#743).** `ChipGroup`
+ (`attributeChipRender.tsx`) gets an explicit `width: fit-content` — its containing row
+ switches to `flex-direction: column` once there's room to form (A10's still-present `@media (min-width: 576px)`), and a flex container's default `align-items: stretch` then stretches
+ any child with no definite cross-size to that column's full width; a definite width makes
+ the chip immune to that stretch regardless of which axis the ancestor row happens to run on.
+ `AttributeChipPanel.tsx`'s flat-stack `LeftArea`/`RightArea` (`BORDER_COLOR_GROUP`/
+ `FRAME_STYLE_GROUP`, two independent axes) now render each group's existing `label` field as
+ a heading above its chip row, plus a divider between them — previously the two groups' rows
+ ran together as one unlabelled list, so a collapse that correctly affects only one group's
+ siblings read as a bug affecting a supposedly single list.
+
D-number scope note: D-numbers are per-proposal in this repo (proposal-h owns
its own D1–D19; the old WTC round used W4–W7). The decisions below are the
**WTC-rebuild round's** ledger, numbered WD1.. to avoid collision with either.
diff --git a/frontend/src/features/attributeChips/AttributeChipPanel.tsx b/frontend/src/features/attributeChips/AttributeChipPanel.tsx
index a49d4d63d..130df8399 100644
--- a/frontend/src/features/attributeChips/AttributeChipPanel.tsx
+++ b/frontend/src/features/attributeChips/AttributeChipPanel.tsx
@@ -73,25 +73,48 @@ const TopArea = styled(ChipRow)`
grid-area: top;
`;
+// Issue #743: BORDER_COLOR_GROUP and FRAME_STYLE_GROUP are independent axes (a card's border
+// colour and its frame era don't imply each other), but nothing previously rendered their
+// `ExclusionGroup.label` - the two groups' chip rows ran together as one unlabelled list, so a
+// user reasonably read a correct half-collapse (one group's siblings dimming, the other
+// untouched) as a bug. Renders the label that already existed as data.
+const GroupHeading = styled.p`
+ margin: 0 0 0.3rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ color: rgba(0, 0, 0, 0.55);
+ text-align: center;
+`;
+
+// A divider between the two exclusion groups (flat-stack layout only, see the `cardSlot == null`
+// branch below) - the ring layout already keeps them apart spatially (left/right of the card),
+// so it doesn't need one.
+const GroupDivider = styled.hr`
+ width: 100%;
+ margin: 0;
+ border: none;
+ border-top: 1px solid rgba(0, 0, 0, 0.12);
+`;
+
// Row+wrap below `sm` (matching TopArea, since the ring hasn't formed yet and there's no
// flanking column to stack vertically inside) - becomes a genuine vertical column only once
// the ring itself forms at `sm` and up.
-const LeftArea = styled(ChipRow)`
- grid-area: left;
-
+const ExclusionChipRow = styled(ChipRow)`
@media (min-width: 576px) {
flex-direction: column;
- align-items: stretch;
}
`;
-const RightArea = styled(ChipRow)`
- grid-area: right;
+// Wraps a heading plus its `ExclusionChipRow` as one unit, so `ChipRing`'s grid can still
+// position the whole group (heading + chips together) as a single "left"/"right" cell.
+const LeftArea = styled.div`
+ grid-area: left;
+`;
- @media (min-width: 576px) {
- flex-direction: column;
- align-items: stretch;
- }
+const RightArea = styled.div`
+ grid-area: right;
`;
// position: relative so an absolutely-positioned burst rendered as part of `cardSlot` (see
@@ -183,16 +206,22 @@ export function AttributeChipPanel({
);
const leftArea = leftGroup != null && (
- {leftGroup.chips.map((chip) =>
- renderAttributeChip(chipArgs, chip.tagName, chip.label)
- )}
+ {leftGroup.label}
+
+ {leftGroup.chips.map((chip) =>
+ renderAttributeChip(chipArgs, chip.tagName, chip.label)
+ )}
+
);
const rightArea = rightGroup != null && (
- {rightGroup.chips.map((chip) =>
- renderAttributeChip(chipArgs, chip.tagName, chip.label)
- )}
+ {rightGroup.label}
+
+ {rightGroup.chips.map((chip) =>
+ renderAttributeChip(chipArgs, chip.tagName, chip.label)
+ )}
+
);
@@ -203,6 +232,7 @@ export function AttributeChipPanel({
{topArea}
{leftArea}
+ {leftArea != null && rightArea != null && }
{rightArea}
>
diff --git a/frontend/src/features/attributeChips/attributeChipRender.tsx b/frontend/src/features/attributeChips/attributeChipRender.tsx
index b16cfa0cd..5fa043965 100644
--- a/frontend/src/features/attributeChips/attributeChipRender.tsx
+++ b/frontend/src/features/attributeChips/attributeChipRender.tsx
@@ -40,6 +40,13 @@ import {
// reclaim the row space the owner reported as dead. Height/tap-targets are unchanged (still the
// full 44px buttons, still directly tappable to override the implied state - see this file's
// header comment).
+// `width: fit-content` (issue #742): an exclusion-group chip renders inside a container
+// (`LeftArea`/`RightArea`, AttributeChipPanel.tsx) that switches to `flex-direction: column`
+// once the ring/flat-stack has room to form; the flex spec's default `align-items: stretch`
+// then stretches any child with no definite cross-size to the column's full width, which is how
+// a chip meant to be a small label+buttons pill ended up spanning the whole panel with its Yes/
+// No buttons stranded far from the label. A definite width makes the chip immune to that stretch
+// regardless of an ancestor's flex-direction.
export const ChipGroup = styled.div<{ impliedNegative: boolean }>`
border: 2px solid rgba(0, 0, 0, 0.25);
border-radius: 0.5rem;
@@ -48,6 +55,7 @@ export const ChipGroup = styled.div<{ impliedNegative: boolean }>`
font-size: 0.85rem;
white-space: nowrap;
min-height: 44px;
+ width: fit-content;
display: inline-flex;
align-items: stretch;
overflow: hidden;