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;