From e277c926c43099bbb5850214e5d2ab7950a053cc Mon Sep 17 00:00:00 2001 From: PelayoFelgueroso Date: Fri, 14 Aug 2026 09:52:33 +0200 Subject: [PATCH 1/5] Change border for outline to avoid wrapping errors --- packages/lib/src/card/utils.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/card/utils.ts b/packages/lib/src/card/utils.ts index 5ac47f2e5..66211c5d4 100644 --- a/packages/lib/src/card/utils.ts +++ b/packages/lib/src/card/utils.ts @@ -20,12 +20,12 @@ export const getCardStyles = ( : "var(--border-width-none)"; const commonStyles = ` - ${mode === "elevated" ? `box-shadow: var(--shadow-100); border: none;` : ``} + ${mode === "elevated" ? `box-shadow: var(--shadow-100);` : ``} ${ mode === "outlined" && !selected - ? `border: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` + ? `outline: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` : selected - ? `border: ${borderWidth} var(--border-style-default) var(--border-color-primary-strong);` + ? `outline: ${borderWidth} var(--border-style-default) var(--border-color-primary-strong);` : `` } ${interactive ? "cursor: pointer;" : ""} @@ -38,16 +38,16 @@ export const getCardStyles = ( outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); outline-offset: calc(var(--border-width-m) * -1);`; active = `box-shadow: var(--shadow-100);`; } else if (mode === "outlined") { - hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; - focus = `border-width: var(--border-width-m); - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + hover = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-medium); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; + focus = `outline-width: var(--border-width-m); + outline-color: var(--border-color-secondary-medium); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; - active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; + active = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-strong); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; } } else if (selected) { - hover = `border-color: var(--border-color-primary-stronger);`; + hover = `outline-color: var(--border-color-primary-stronger);`; focus = `outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; - active = `border-color: var(--border-color-primary-strong);`; + active = `outline-color: var(--border-color-primary-strong);`; } return ` From ec02cc0e450ffee4f6586a841f64e9d4d6f2bffd Mon Sep 17 00:00:00 2001 From: PelayoFelgueroso Date: Wed, 26 Aug 2026 13:41:21 +0200 Subject: [PATCH 2/5] fix card focus --- packages/lib/src/card/Card.tsx | 40 ++++++++++++++----- packages/lib/src/card/utils.ts | 71 +++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 37 deletions(-) diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index 2c7fb92bf..dd59558b0 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -2,7 +2,7 @@ import { forwardRef, Ref, useContext, useEffect, useState } from "react"; import styled from "@emotion/styled"; import CardPropsType from "./types"; import DxcImage from "../image/Image"; -import { getCardStyles, emptyIconSizes, handleEvent } from "./utils"; +import { getCardStyles, emptyIconSizes, handleEvent, getSelectableWrapperStyles } from "./utils"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import DxcTypography from "../typography/Typography"; @@ -12,19 +12,26 @@ const Card = styled.div<{ mode: CardPropsType["mode"]; direction?: CardPropsType["direction"]; imagePosition?: CardPropsType["imagePosition"]; + selectable?: boolean; interactive?: boolean; size?: CardPropsType["size"]; - selected?: boolean; }>` display: flex; flex-direction: ${({ direction, imagePosition }) => imagePosition === "before" ? direction : `${direction}-reverse`}; gap: var(--spacing-gap-s); + padding: ${({ selectable }) => (selectable ? "var(--spacing-padding-none)" : "var(--spacing-padding-xs)")}; box-sizing: border-box; - padding: var(--spacing-padding-xs); border-radius: var(--border-radius-l); - ${({ mode, interactive, selected, size }) => getCardStyles(mode, interactive ?? false, selected, size)} + ${({ mode, interactive, size }) => getCardStyles(mode, interactive ?? false, size)} + outline-offset: calc(var(--border-width-m)); background: var(--color-bg-neutral-lightest); +`; + +const SelectableWrapper = styled.div<{ selected: boolean; mode: CardPropsType["mode"] }>` + ${({ selected, mode }) => getSelectableWrapperStyles(selected, mode)} + border-radius: var(--border-radius-l); + padding: var(--spacing-padding-xs); overflow: hidden; `; @@ -190,8 +197,8 @@ const DxcCard = forwardRef( size={size} imagePosition={imagePosition} mode={mode} + selectable={selectable} interactive={isInteractive} - selected={selectable && internalSelected} tabIndex={isInteractive ? tabIndex : undefined} role={selectable ? "checkbox" : isInteractive ? "button" : undefined} onClick={(event) => { @@ -203,12 +210,25 @@ const DxcCard = forwardRef( aria-checked={selectable ? internalSelected : undefined} ref={ref as Ref} > - {image && ( - - - + {selectable ? ( + + {image && ( + + + + )} + {children} + + ) : ( + <> + {image && ( + + + + )} + {children} + )} - {children} ); } diff --git a/packages/lib/src/card/utils.ts b/packages/lib/src/card/utils.ts index 66211c5d4..c26424076 100644 --- a/packages/lib/src/card/utils.ts +++ b/packages/lib/src/card/utils.ts @@ -1,18 +1,44 @@ import CardPropsType from "./types"; -const calculateSize = (size?: CardPropsType["size"], selected?: boolean, borderWidth?: string, outlined?: boolean) => { - return `width: ${size?.width === "fillParent" ? "100%" : selected || outlined ? `calc-size(fit-content, size - (${borderWidth} * 2))` : "fit-content"}; - height: ${size?.height === "fillParent" ? "100%" : selected || outlined ? `calc-size(fit-content, size - (${borderWidth} * 2))` : "fit-content"};`; +const calculateSize = (size?: CardPropsType["size"]) => { + return `width: ${size?.width === "fillParent" ? "100%" : "fit-content"}; + height: ${size?.height === "fillParent" ? "100%" : "fit-content"};`; }; -export const getCardStyles = ( - mode: CardPropsType["mode"], - interactive: boolean, - selected?: boolean, - size?: CardPropsType["size"] -) => { +export const getCardStyles = (mode: CardPropsType["mode"], interactive: boolean, size?: CardPropsType["size"]) => { let hover = ""; let focus = ""; let active = ""; + + const commonStyles = ` + ${mode === "elevated" ? `box-shadow: var(--shadow-100); border: none;` : ``} + ${interactive ? "cursor: pointer;" : ""} + ${calculateSize(size)} + `; + if (mode === "elevated") { + hover = `box-shadow: var(--shadow-300);`; + focus = `box-shadow: var(--shadow-100); + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `box-shadow: var(--shadow-100);`; + } else if (mode === "outlined") { + hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium);`; + focus = `border-width: var(--border-width-m); + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong);`; + } + + return ` + ${commonStyles} + &:hover { ${hover} } + &:focus { ${focus} } + &:active { ${active} } + `; +}; + +export const getSelectableWrapperStyles = (selected: boolean, mode: CardPropsType["mode"]) => { + let hover = ""; + let focus = ""; + let active = ""; + const borderWidth = selected ? "var(--border-width-m)" : mode === "outlined" @@ -20,29 +46,19 @@ export const getCardStyles = ( : "var(--border-width-none)"; const commonStyles = ` - ${mode === "elevated" ? `box-shadow: var(--shadow-100);` : ``} ${ mode === "outlined" && !selected ? `outline: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` - : selected - ? `outline: ${borderWidth} var(--border-style-default) var(--border-color-primary-strong);` - : `` + : "" } - ${interactive ? "cursor: pointer;" : ""} - ${calculateSize(size, selected, borderWidth, mode === "outlined")} `; - if (interactive && !selected) { - if (mode === "elevated") { - hover = `box-shadow: var(--shadow-300);`; - focus = `box-shadow: var(--shadow-100); - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); outline-offset: calc(var(--border-width-m) * -1);`; - active = `box-shadow: var(--shadow-100);`; - } else if (mode === "outlined") { - hover = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-medium); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; + + if (!selected) { + if (mode === "outlined") { + hover = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-medium);`; focus = `outline-width: var(--border-width-m); - outline-color: var(--border-color-secondary-medium); - ${calculateSize(size, selected, "var(--border-width-m)", true)}`; - active = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-strong); ${calculateSize(size, selected, "var(--border-width-m)", true)}`; + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-strong);`; } } else if (selected) { hover = `outline-color: var(--border-color-primary-stronger);`; @@ -55,7 +71,8 @@ export const getCardStyles = ( &:hover { ${hover} } &:focus { ${focus} } &:active { ${active} } - `; + outline: ${selected ? "var(--border-width-m) var(--border-style-default) var(--border-color-primary-strong)" : ""}; + `; }; export const handleEvent = ( From f62b3c70daee8830e4f0c8a136187051d8d1bef3 Mon Sep 17 00:00:00 2001 From: PelayoFelgueroso Date: Wed, 26 Aug 2026 14:23:59 +0200 Subject: [PATCH 3/5] fix outlined --- packages/lib/src/card/Card.tsx | 13 ++++++---- packages/lib/src/card/utils.ts | 45 +++++++++++++++------------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index dd59558b0..436b11644 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -14,6 +14,7 @@ const Card = styled.div<{ imagePosition?: CardPropsType["imagePosition"]; selectable?: boolean; interactive?: boolean; + selected?: boolean; size?: CardPropsType["size"]; }>` display: flex; @@ -23,13 +24,14 @@ const Card = styled.div<{ padding: ${({ selectable }) => (selectable ? "var(--spacing-padding-none)" : "var(--spacing-padding-xs)")}; box-sizing: border-box; border-radius: var(--border-radius-l); - ${({ mode, interactive, size }) => getCardStyles(mode, interactive ?? false, size)} - outline-offset: calc(var(--border-width-m)); + border-style: var(--border-style-default); + ${({ mode, interactive, selected, size }) => getCardStyles(mode, interactive ?? false, selected, size)} + outline-offset: var(--border-width-m); background: var(--color-bg-neutral-lightest); `; -const SelectableWrapper = styled.div<{ selected: boolean; mode: CardPropsType["mode"] }>` - ${({ selected, mode }) => getSelectableWrapperStyles(selected, mode)} +const SelectableWrapper = styled.div<{ selected: boolean }>` + ${({ selected }) => getSelectableWrapperStyles(selected)} border-radius: var(--border-radius-l); padding: var(--spacing-padding-xs); overflow: hidden; @@ -198,6 +200,7 @@ const DxcCard = forwardRef( imagePosition={imagePosition} mode={mode} selectable={selectable} + selected={selectable && internalSelected} interactive={isInteractive} tabIndex={isInteractive ? tabIndex : undefined} role={selectable ? "checkbox" : isInteractive ? "button" : undefined} @@ -211,7 +214,7 @@ const DxcCard = forwardRef( ref={ref as Ref} > {selectable ? ( - + {image && ( diff --git a/packages/lib/src/card/utils.ts b/packages/lib/src/card/utils.ts index c26424076..187c3f9df 100644 --- a/packages/lib/src/card/utils.ts +++ b/packages/lib/src/card/utils.ts @@ -4,16 +4,33 @@ const calculateSize = (size?: CardPropsType["size"]) => { return `width: ${size?.width === "fillParent" ? "100%" : "fit-content"}; height: ${size?.height === "fillParent" ? "100%" : "fit-content"};`; }; -export const getCardStyles = (mode: CardPropsType["mode"], interactive: boolean, size?: CardPropsType["size"]) => { +export const getCardStyles = ( + mode: CardPropsType["mode"], + interactive: boolean, + selected?: boolean, + size?: CardPropsType["size"] +) => { let hover = ""; let focus = ""; let active = ""; + const borderWidth = selected + ? "var(--border-width-m)" + : mode === "outlined" + ? "var(--border-width-s)" + : "var(--border-width-none)"; + const commonStyles = ` ${mode === "elevated" ? `box-shadow: var(--shadow-100); border: none;` : ``} + ${ + mode === "outlined" && !selected + ? `border: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` + : "" + } ${interactive ? "cursor: pointer;" : ""} ${calculateSize(size)} `; + if (mode === "elevated") { hover = `box-shadow: var(--shadow-300);`; focus = `box-shadow: var(--shadow-100); @@ -34,40 +51,18 @@ export const getCardStyles = (mode: CardPropsType["mode"], interactive: boolean, `; }; -export const getSelectableWrapperStyles = (selected: boolean, mode: CardPropsType["mode"]) => { +export const getSelectableWrapperStyles = (selected: boolean) => { let hover = ""; let focus = ""; let active = ""; - const borderWidth = selected - ? "var(--border-width-m)" - : mode === "outlined" - ? "var(--border-width-s)" - : "var(--border-width-none)"; - - const commonStyles = ` - ${ - mode === "outlined" && !selected - ? `outline: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` - : "" - } - `; - - if (!selected) { - if (mode === "outlined") { - hover = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-medium);`; - focus = `outline-width: var(--border-width-m); - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; - active = `outline-width: var(--border-width-m); outline-color: var(--border-color-neutral-strong);`; - } - } else if (selected) { + if (selected) { hover = `outline-color: var(--border-color-primary-stronger);`; focus = `outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; active = `outline-color: var(--border-color-primary-strong);`; } return ` - ${commonStyles} &:hover { ${hover} } &:focus { ${focus} } &:active { ${active} } From ee375ce9e69ff79f4e0daa37ab9ff88be46ab9cf Mon Sep 17 00:00:00 2001 From: PelayoFelgueroso Date: Wed, 26 Aug 2026 14:26:26 +0200 Subject: [PATCH 4/5] fix black border --- packages/lib/src/card/Card.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index 436b11644..954af4bd7 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -25,6 +25,7 @@ const Card = styled.div<{ box-sizing: border-box; border-radius: var(--border-radius-l); border-style: var(--border-style-default); + border-color: transparent; ${({ mode, interactive, selected, size }) => getCardStyles(mode, interactive ?? false, selected, size)} outline-offset: var(--border-width-m); background: var(--color-bg-neutral-lightest); From 11ec0dd79877c3998eae8c6f951a681d7834020c Mon Sep 17 00:00:00 2001 From: PelayoFelgueroso Date: Wed, 26 Aug 2026 14:47:46 +0200 Subject: [PATCH 5/5] fix outline focus --- packages/lib/src/card/utils.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/card/utils.ts b/packages/lib/src/card/utils.ts index 187c3f9df..ca7ab96d8 100644 --- a/packages/lib/src/card/utils.ts +++ b/packages/lib/src/card/utils.ts @@ -25,22 +25,28 @@ export const getCardStyles = ( ${ mode === "outlined" && !selected ? `border: ${borderWidth} var(--border-style-default) var(--border-color-neutral-light);` - : "" + : "border-width: var(--border-width-none);" } ${interactive ? "cursor: pointer;" : ""} ${calculateSize(size)} `; - if (mode === "elevated") { - hover = `box-shadow: var(--shadow-300);`; - focus = `box-shadow: var(--shadow-100); + if (interactive && !selected) { + if (mode === "elevated") { + hover = `box-shadow: var(--shadow-300);`; + focus = `box-shadow: var(--shadow-100); outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; - active = `box-shadow: var(--shadow-100);`; - } else if (mode === "outlined") { - hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium);`; - focus = `border-width: var(--border-width-m); + active = `box-shadow: var(--shadow-100);`; + } else if (mode === "outlined") { + hover = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-medium);`; + focus = `border-width: var(--border-width-m); outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; - active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong);`; + active = `border-width: var(--border-width-m); border-color: var(--border-color-neutral-strong);`; + } + } else if (interactive && selected && mode === "outlined") { + hover = `border-width: var(--border-width-none);`; + focus = `border-width: var(--border-width-none); outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);`; + active = `border-width: var(--border-width-none);`; } return `