Handoff: CardSelect
What and why
The PDS Card Select with a shadow prop. Every card has a drop shadow today; in dense layouts and on surfaces that already carry elevation it is too heavy. shadow={true} (default) is the unchanged PDS look. shadow={false} removes the shadow and draws the card with a border in the input border color, a background tint on hover, and, when the radio indicator is hidden, a focus-color ring on the selected card. The proposal wraps the published CardSelect; PDS is not changed.
- Figma: Card Select page in PDS-Proposals, with two real components:
Card select item(set665:265:ShadowOn/Off xStateDefault/Hover/Selected xShow IndicatorTrue/False, built like the library item with a placeholder slot and no text) andCard Select(666:213: a label and a row of three exposed cards). Jira: none. Source: a brief from the designer, 2026-11-03. - Context:
context.md
Exploration and decisions
- Wrap, not extend. Same concept and role, only the look changes; the markup, radio inputs and keyboard behavior come from PDS. The port adds one prop and some CSS (see Port notes).
- A border replaces the shadow. The PDS raised shadow already includes a 1px edge ring, so removing it alone leaves a white card invisible on a white surface. The border uses
--pds-color-border-input, so a card reads like the other form controls. - Hover is the secondary Button hover.
--pds-color-button-secondary-background-hover, layered over the card background (background-image), so thedefault,secondaryandtransparentbackgrounds keep their fill. - The blue outline is saved for the no-indicator state. With the indicator, the check icon marks the selected card and the border stays; without it, the focus-color ring does.
- Figma follows Storybook, not the PDS Figma library. The library
Card select itempads 16 on all sides; the PDS code pads 20 and 24. The Figma item pads 20 and 24 (primitive/spacing/LandXL). Design: the library item should be updated to match the code. - The shadow is on the card. In Storybook
shadowis a prop ofCardSelectand applies to every card; in FigmaShadowis a property of the item and is set per card. - Figma indicator color is a token-bound fill on the library
circleicon (semantic/border/input; the success fill oncircleCheck), because the icon has no color property. Show Indicatoris a variant in Figma, not a boolean, because it changes the selected card's border as well as the circle.- Tried and not done: a prop on the card (the shadow is on each card, but the group is the API).
API
| Prop | Type | Default | Purpose |
|---|---|---|---|
shadow |
boolean |
true |
Show the drop shadow on each card. false: border in the input color, hover tint, and the focus ring on the selected card when showIndicator is false. |
Everything else is the PDS CardSelect (background, defaultValue, id, imageVariant, label, labelDisplay, layout, onChange, options, padding, showCardLabels, showIndicator, size).
Variants and states: shadow on, shadow off, shadow off without the indicator, shadow off at size l; unselected, hover, selected.
Tokens used
--pds-border-width-default--pds-color-border-input--pds-color-button-secondary-background-hover--pds-color-interactive-focus
The rest come from the PDS Card Select itself.
Impact
From impact.md: nothing reaches existing components; the additions are scoped to .pds-card-select--no-shadow. No other proposal imports or uses this one.
Port order (docs/PORT-ORDER.md): later, 17th, an extension of the PDS Card Select. It is not tagged ready for dev yet.
Reused vs net-new
- Reused: the PDS
CardSelect(unchanged), all its props and its tokens. - Net-new: the prop
shadowand the modifier.pds-card-select--no-shadow(seecustom.md).
Accessibility
- The radio inputs, labels, legend and keyboard behavior are the PDS Card Select's. Arrow keys move the selection; the focus ring (outline, 2px offset) is drawn on the card when its radio is focused, in both modes.
- The unselected border (
--pds-color-border-input) is about 1.5:1 against a white surface, below the 3:1 usually asked of a control boundary. It is the same token the PDS inputs use, and the selected state does not rely on it (check icon, or the focus ring). Noted for the designer; unchanged. - axe-core (the engine behind the Storybook a11y panel) found no violations, 2026-11-03: all four stories (With Shadow, Without Shadow, Without Shadow Or Indicator, Without Shadow Large) in light and dark. Also checked: computed styles of all stories in light mode and the dark values; keyboard focus on the no-indicator story; 375px width with no horizontal scroll. Not checked: dark-mode hover and ring colors in the Theme toolbar by eye, and very long card text.
Decisions and open questions
- Not built in the Figma item:
Size,Disabled,background,padding,imageVariant,Show Card Labels(they exist in Storybook and the PDS component).
Port notes
- Existing component, in
pds-toolkit-reactundersrc/components/cards/CardSelect/. - Add
shadow?: boolean(defaulttrue) toCardSelectProps, TSDoc included, andconst noShadowClass = !shadow ? \${baseClass}--no-shadow` : null` to the class list. - Add the three
.pds-card-select--no-shadowrules fromcard-select.cssto the component'scard-select.css. The doubled.pds-card-select__option-label.pds-card-select__option-labelselector is only here to beat the package rules from outside; in the port, write them as normal single selectors after the existing ones, and keep the ring scoped to--no-indicator. - Update the Card Select docs and add a story.
Preview
npm run storybook, stories Component Proposals/Card Select. Figma: Card Select, page Card Select.