From c1005fec09a775ce2bb4ef4436ec915d2387c8ff Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 3 Dec 2025 10:11:17 +0100 Subject: [PATCH 01/33] chore: Refactor card into separate internal component --- src/cards/index.tsx | 103 +++++++++--------- src/cards/styles.scss | 43 +------- src/internal/components/card/index.tsx | 49 +++++++++ src/internal/components/card/interfaces.ts | 56 ++++++++++ .../components/card}/motion.scss | 4 +- src/internal/components/card/styles.scss | 95 ++++++++++++++++ src/test-utils/dom/cards/index.ts | 5 +- 7 files changed, 262 insertions(+), 93 deletions(-) create mode 100644 src/internal/components/card/index.tsx create mode 100644 src/internal/components/card/interfaces.ts rename src/{cards => internal/components/card}/motion.scss (89%) create mode 100644 src/internal/components/card/styles.scss diff --git a/src/cards/index.tsx b/src/cards/index.tsx index 93207b5624..a60e217e71 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -12,6 +12,7 @@ import { InternalContainerAsSubstep } from '../container/internal'; import { useInternalI18n } from '../i18n/context'; import { AnalyticsFunnelSubStep } from '../internal/analytics/components/analytics-funnel'; import { getBaseProps } from '../internal/base-component'; +import Card from '../internal/components/card'; import { CollectionLabelContext } from '../internal/context/collection-label-context'; import { LinkDefaultVariantContext } from '../internal/context/link-default-variant-context'; import useBaseComponent from '../internal/hooks/use-base-component'; @@ -269,7 +270,6 @@ const CardsList = ({ }) => { const selectable = !!selectionType; const canClickEntireCard = selectable && entireCardClickable; - const isRefresh = useVisualRefresh(); const { moveFocusDown, moveFocusUp } = useSelectionFocusMove(selectionType, items.length); @@ -315,60 +315,61 @@ const CardsList = ({ }, }; return ( -
  • + + + ) + } + active={selectable && selected} + className={styles.card} + header={ +
    + {cardDefinition.header ? cardDefinition.header(item) : ''} +
    + } + innerMetadataAttributes={ + entireCardClickable && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {} + } + key={index} + metadataAttributes={{ + ...getAnalyticsMetadataAttribute({ + component: { + innerContext: { + position: `${index + 1}`, + item: `${key}`, + }, + }, + }), + ...focusMarkers.item, + }} + onClick={ + canClickEntireCard + ? event => { + selectionProps?.onChange(); + // Manually move focus to the native input (checkbox or radio button) + event.currentTarget.querySelector('input')?.focus(); + } + : undefined + } onFocus={onFocus} - {...(focusMarkers && focusMarkers.item)} role={listItemRole} - {...getAnalyticsMetadataAttribute({ - component: { - innerContext: { - position: `${index + 1}`, - item: `${key}`, - }, - }, - })} + TagName="li" > -
    { - selectionProps?.onChange(); - // Manually move focus to the native input (checkbox or radio button) - event.currentTarget.querySelector('input')?.focus(); - } - : undefined - } - > -
    -
    - {cardDefinition.header ? cardDefinition.header(item) : ''} -
    - {selectionProps && ( -
    - -
    - )} + {visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => ( +
    + {header ?
    {header}
    : ''} + {content ?
    {content(item)}
    : ''}
    - {visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => ( -
    - {header ?
    {header}
    : ''} - {content ?
    {content(item)}
    : ''} -
    - ))} -
    -
  • + ))} + ); })} diff --git a/src/cards/styles.scss b/src/cards/styles.scss index 02c6a97dcc..dceb23945c 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -7,47 +7,9 @@ @use '../internal/styles' as styles; @use '../internal/styles/tokens' as awsui; -@use '../container/shared' as container; -@use './motion'; - -@mixin card-style { - border-start-start-radius: awsui.$border-radius-container; - border-start-end-radius: awsui.$border-radius-container; - border-end-start-radius: awsui.$border-radius-container; - border-end-end-radius: awsui.$border-radius-container; - box-sizing: border-box; - - &::before { - @include styles.base-pseudo-element; - // Reset border color to prevent it from flashing black during card selection animation - border-color: transparent; - border-block-start: awsui.$border-container-top-width solid awsui.$color-border-container-top; - border-start-start-radius: awsui.$border-radius-container; - border-start-end-radius: awsui.$border-radius-container; - border-end-start-radius: awsui.$border-radius-container; - border-end-end-radius: awsui.$border-radius-container; - z-index: 1; - } - - &::after { - @include styles.base-pseudo-element; - border-start-start-radius: awsui.$border-radius-container; - border-start-end-radius: awsui.$border-radius-container; - border-end-start-radius: awsui.$border-radius-container; - border-end-end-radius: awsui.$border-radius-container; - } - &:not(.refresh)::after { - box-shadow: awsui.$shadow-container; - } - &.refresh::after { - border-block: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; - border-inline: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; - } -} .root { @include styles.styles-reset(); - @include styles.default-text-style; } .header { @@ -111,6 +73,7 @@ } } +<<<<<<< HEAD .card { display: flex; overflow-wrap: break-word; @@ -155,6 +118,10 @@ } } } +======= +.card-header { + @include styles.font-heading-m; +>>>>>>> 781f5e623 (chore: Refactor card into separate internal component) } .section { diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx new file mode 100644 index 0000000000..29ed5aaf7e --- /dev/null +++ b/src/internal/components/card/index.tsx @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import clsx from 'clsx'; + +import { useVisualRefresh } from '../../hooks/use-visual-mode'; +import { InternalCardProps } from './interfaces'; + +import styles from './styles.css.js'; + +export default function Card({ + action, + active, + children, + className, + header, + innerMetadataAttributes, + metadataAttributes, + onClick, + onFocus, + role, + TagName = 'div', +}: InternalCardProps) { + const isRefresh = useVisualRefresh(); + + return ( + +
    +
    +
    {header}
    + {action &&
    {action}
    } +
    + {children} +
    +
    + ); +} diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts new file mode 100644 index 0000000000..c37d4c2d61 --- /dev/null +++ b/src/internal/components/card/interfaces.ts @@ -0,0 +1,56 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { FocusEventHandler } from 'react'; + +import { BaseComponentProps } from '../../base-component'; + +export interface InternalCardProps extends BaseComponentProps { + /** + * Specifies an action for the card. + * It is recommended to use a button with inline-icon variant. + */ + action?: React.ReactNode; + + /** + * Specifies whether the card is in active state. + */ + active?: boolean; + + /** + * Optional URL for an image which will be displayed cropped as a background of the card. + * When this property is used, a dark gradient is overlayed and the text above defaults to bright colors. + * Make sure that any content you place on the card has sufficient contrast with the overlayed image behind. + */ + imageUrl?: string; + + /** + * Primary content displayed in the card. + */ + children?: React.ReactNode; + + /** + * Heading text. + */ + header?: React.ReactNode; + + /** + * Icon which will be displayed at the top of the card, + * inline at the start of the content. + */ + icon?: React.ReactNode; + + /** + * Called when the user clicks on the card. + */ + onClick?: React.MouseEventHandler; + + onFocus?: FocusEventHandler; + + role?: string; + + TagName?: 'li' | 'div'; + + metadataAttributes: Record; + + innerMetadataAttributes: Record; +} diff --git a/src/cards/motion.scss b/src/internal/components/card/motion.scss similarity index 89% rename from src/cards/motion.scss rename to src/internal/components/card/motion.scss index 385aa1d4b2..4dcf4658d3 100644 --- a/src/cards/motion.scss +++ b/src/internal/components/card/motion.scss @@ -3,8 +3,8 @@ SPDX-License-Identifier: Apache-2.0 */ -@use '../internal/styles' as styles; -@use '../internal/styles/tokens' as awsui; +@use '../../styles' as styles; +@use '../../styles/tokens' as awsui; .card-inner { @include styles.with-motion { diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss new file mode 100644 index 0000000000..acf287b3a6 --- /dev/null +++ b/src/internal/components/card/styles.scss @@ -0,0 +1,95 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 +*/ + +@use 'sass:math'; + +@use '../../styles' as styles; +@use '../../styles/tokens' as awsui; +@use './motion'; + +@mixin card-style { + border-start-start-radius: awsui.$border-radius-container; + border-start-end-radius: awsui.$border-radius-container; + border-end-start-radius: awsui.$border-radius-container; + border-end-end-radius: awsui.$border-radius-container; + box-sizing: border-box; + + &::before { + @include styles.base-pseudo-element; + // Reset border color to prevent it from flashing black during card selection animation + border-color: transparent; + border-block-start: awsui.$border-container-top-width solid awsui.$color-border-container-top; + border-start-start-radius: awsui.$border-radius-container; + border-start-end-radius: awsui.$border-radius-container; + border-end-start-radius: awsui.$border-radius-container; + border-end-end-radius: awsui.$border-radius-container; + z-index: 1; + } + + &::after { + @include styles.base-pseudo-element; + border-start-start-radius: awsui.$border-radius-container; + border-start-end-radius: awsui.$border-radius-container; + border-end-start-radius: awsui.$border-radius-container; + border-end-end-radius: awsui.$border-radius-container; + } + &:not(.refresh)::after { + box-shadow: awsui.$shadow-container; + } + &.refresh::after { + border-block: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; + border-inline: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; + } +} + +.root { + @include styles.styles-reset(); +} + +.card { + display: flex; + overflow-wrap: break-word; + word-wrap: break-word; + box-sizing: border-box; + margin-block: 0; + margin-inline: 0; + padding-block-start: 0; + padding-block-end: awsui.$space-grid-gutter; + padding-inline-start: awsui.$space-grid-gutter; + padding-inline-end: 0; + list-style: none; + &-inner { + position: relative; + background-color: awsui.$color-background-container-content; + margin-block: 0; + margin-inline: 0; + padding-block: awsui.$space-card-vertical; + padding-inline: awsui.$space-card-horizontal; + inline-size: 100%; + min-inline-size: 0; + @include card-style; + } + &-header { + @include styles.font-heading-m; + &-inner { + inline-size: 100%; + display: inline-block; + } + } + &-with-action { + > .card-inner > .card-header { + inline-size: 90%; + } + } + &-active { + > .card-inner { + background-color: awsui.$color-background-item-selected; + &::before { + border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; + border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; + } + } + } +} diff --git a/src/test-utils/dom/cards/index.ts b/src/test-utils/dom/cards/index.ts index adc16584c3..13b8277a87 100644 --- a/src/test-utils/dom/cards/index.ts +++ b/src/test-utils/dom/cards/index.ts @@ -8,6 +8,7 @@ import PaginationWrapper from '../pagination'; import TextFilterWrapper from '../text-filter'; import styles from '../../../cards/styles.selectors.js'; +import cardStyles from '../../../internal/components/card/styles.selectors.js'; import tableStyles from '../../../table/styles.selectors.js'; class CardSectionWrapper extends ComponentWrapper { @@ -31,7 +32,7 @@ class CardWrapper extends ComponentWrapper { } findCardHeader(): ElementWrapper | null { - return this.findByClassName(styles['card-header-inner']); + return this.findByClassName(cardStyles['card-header-inner']); } findSelectionArea(): ElementWrapper | null { @@ -49,7 +50,7 @@ export default class CardsWrapper extends ComponentWrapper { } findSelectedItems(): Array { - return this.findAllByClassName(styles['card-selected']).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(cardStyles['card-active']).map(c => new CardWrapper(c.getElement())); } findHeader(): ElementWrapper | null { From b5c2aa014f21320d2d721b931e1d28336cf83276 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 3 Dec 2025 18:11:00 +0100 Subject: [PATCH 02/33] Fix analytics metadata --- src/cards/index.tsx | 12 ++++++++---- src/internal/components/card/index.tsx | 2 +- src/internal/components/card/styles.scss | 3 --- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cards/index.tsx b/src/cards/index.tsx index a60e217e71..7e7a00392f 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -331,12 +331,16 @@ const CardsList = ({ active={selectable && selected} className={styles.card} header={ -
    - {cardDefinition.header ? cardDefinition.header(item) : ''} -
    + cardDefinition.header ? ( +
    + {cardDefinition.header(item)} +
    + ) : ( + '' + ) } innerMetadataAttributes={ - entireCardClickable && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {} + canClickEntireCard && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {} } key={index} metadataAttributes={{ diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index 29ed5aaf7e..b9fa73119c 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -25,7 +25,7 @@ export default function Card({ return ( Date: Wed, 3 Dec 2025 18:14:36 +0100 Subject: [PATCH 03/33] Minor refactor --- src/cards/index.tsx | 2 +- src/cards/styles.scss | 51 ++------------------ src/internal/components/card/index.tsx | 10 ++-- src/internal/components/card/interfaces.ts | 2 +- src/internal/components/card/styles.scss | 56 ++++++++++++---------- src/test-utils/dom/cards/index.ts | 6 +-- 6 files changed, 44 insertions(+), 83 deletions(-) diff --git a/src/cards/index.tsx b/src/cards/index.tsx index 7e7a00392f..6ea70bbf35 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -365,7 +365,7 @@ const CardsList = ({ } onFocus={onFocus} role={listItemRole} - TagName="li" + tagName="li" > {visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => (
    diff --git a/src/cards/styles.scss b/src/cards/styles.scss index dceb23945c..a496a4e859 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -73,55 +73,12 @@ } } -<<<<<<< HEAD -.card { - display: flex; - overflow-wrap: break-word; - word-wrap: break-word; - box-sizing: border-box; - margin-block: 0; - margin-inline: 0; - padding-block-start: 0; - padding-block-end: awsui.$space-grid-gutter; - padding-inline-start: awsui.$space-grid-gutter; - padding-inline-end: 0; - list-style: none; - &-inner { - position: relative; - background-color: awsui.$color-background-container-content; - margin-block: 0; - margin-inline: 0; - padding-block: awsui.$space-card-vertical; - padding-inline: awsui.$space-card-horizontal; - inline-size: 100%; - min-inline-size: 0; - @include card-style; - } - &-header { - @include styles.font-heading-m; - &-inner { - inline-size: 100%; - display: inline-block; - } - } - &-selectable { - > .card-inner > .card-header { - inline-size: 90%; - } - } - &-selected { - > .card-inner { - background-color: awsui.$color-background-item-selected; - &::before { - border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; - border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; - } - } - } -======= +.card-header-inner { + /* Used in test utils */ +} + .card-header { @include styles.font-heading-m; ->>>>>>> 781f5e623 (chore: Refactor card into separate internal component) } .section { diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index b9fa73119c..d8632843a2 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -19,15 +19,15 @@ export default function Card({ onClick, onFocus, role, - TagName = 'div', + tagName: TagName = 'div', }: InternalCardProps) { const isRefresh = useVisualRefresh(); return ( -
    -
    {header}
    +
    +
    {header}
    {action &&
    {action}
    }
    {children} diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index c37d4c2d61..159f8fc505 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -48,7 +48,7 @@ export interface InternalCardProps extends BaseComponentProps { role?: string; - TagName?: 'li' | 'div'; + tagName?: 'li' | 'div'; metadataAttributes: Record; diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 418e0b6aa3..a40f933e82 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -57,36 +57,40 @@ padding-inline-start: awsui.$space-grid-gutter; padding-inline-end: 0; list-style: none; +} + +.card-inner { + position: relative; + background-color: awsui.$color-background-container-content; + margin-block: 0; + margin-inline: 0; + padding-block: awsui.$space-card-vertical; + padding-inline: awsui.$space-card-horizontal; + inline-size: 100%; + min-inline-size: 0; + @include card-style; +} + +.header { + @include styles.font-heading-m; &-inner { - position: relative; - background-color: awsui.$color-background-container-content; - margin-block: 0; - margin-inline: 0; - padding-block: awsui.$space-card-vertical; - padding-inline: awsui.$space-card-horizontal; inline-size: 100%; - min-inline-size: 0; - @include card-style; + display: inline-block; } - &-header { - @include styles.font-heading-m; - &-inner { - inline-size: 100%; - display: inline-block; - } - } - &-with-action { - > .card-inner > .card-header { - inline-size: 90%; - } +} + +.with-action { + > .card-inner > .card-header { + inline-size: 90%; } - &-active { - > .card-inner { - background-color: awsui.$color-background-item-selected; - &::before { - border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; - border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; - } +} + +.active { + > .card-inner { + background-color: awsui.$color-background-item-selected; + &::before { + border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; + border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; } } } diff --git a/src/test-utils/dom/cards/index.ts b/src/test-utils/dom/cards/index.ts index 13b8277a87..574a6f7250 100644 --- a/src/test-utils/dom/cards/index.ts +++ b/src/test-utils/dom/cards/index.ts @@ -32,7 +32,7 @@ class CardWrapper extends ComponentWrapper { } findCardHeader(): ElementWrapper | null { - return this.findByClassName(cardStyles['card-header-inner']); + return this.find(`:is(.${cardStyles['header-inner']}, .${styles['card-header-inner']})`); } findSelectionArea(): ElementWrapper | null { @@ -46,11 +46,11 @@ export default class CardsWrapper extends ComponentWrapper { private containerWrapper = new ContainerWrapper(this.getElement()); findItems(): Array { - return this.findAllByClassName(styles.card).map(c => new CardWrapper(c.getElement())); + return this.findAll(`:is(.${cardStyles.root}, .${styles.card})`).map(c => new CardWrapper(c.getElement())); } findSelectedItems(): Array { - return this.findAllByClassName(cardStyles['card-active']).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(cardStyles.active).map(c => new CardWrapper(c.getElement())); } findHeader(): ElementWrapper | null { From da33c0be98816077ca79f3e5118b44cdb7b341cc Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 14 Jan 2026 19:16:55 +0100 Subject: [PATCH 04/33] Update test utils selectors --- .../__snapshots__/test-utils-selectors.test.tsx.snap | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap index 86add834db..ce643df1e7 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap @@ -117,7 +117,6 @@ exports[`test-utils selectors 1`] = ` ], "cards": [ "awsui_card-header-inner_p8a6i", - "awsui_card-selected_p8a6i", "awsui_card_p8a6i", "awsui_empty_p8a6i", "awsui_loading_p8a6i", @@ -357,6 +356,7 @@ exports[`test-utils selectors 1`] = ` ], "internal": [ "awsui_actions_x6dl3", + "awsui_active_1xxz5", "awsui_application_1fcus", "awsui_axis-label--x_f0fot", "awsui_axis-label--y_f0fot", @@ -381,6 +381,7 @@ exports[`test-utils selectors 1`] = ` "awsui_filtering-match-highlight_1p2cx", "awsui_footer_dgs8z", "awsui_has-background_15o6u", + "awsui_header-inner_1xxz5", "awsui_header_dgs8z", "awsui_highlighted_15o6u", "awsui_icon_x6dl3", @@ -405,6 +406,7 @@ exports[`test-utils selectors 1`] = ` "awsui_root_1om0h", "awsui_root_1qprf", "awsui_root_1t44z", + "awsui_root_1xxz5", "awsui_root_qwoo0", "awsui_root_vrgzu", "awsui_screenreader-content_15o6u", From 4fecd25f7b970b85ec09230a7065b53f47f8da27 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 15 Jan 2026 12:26:14 +0100 Subject: [PATCH 05/33] Add preview example --- pages/card/preview.page.tsx | 46 ++++++++++++++++++++++ src/cards/styles.scss | 1 + src/internal/components/card/index.tsx | 3 +- src/internal/components/card/interfaces.ts | 6 ++- src/internal/components/card/styles.scss | 15 ++++++- 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 pages/card/preview.page.tsx diff --git a/pages/card/preview.page.tsx b/pages/card/preview.page.tsx new file mode 100644 index 0000000000..493ca97b40 --- /dev/null +++ b/pages/card/preview.page.tsx @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import ButtonGroup from '~components/button-group'; +import Card from '~components/internal/components/card'; + +import image from '../container/images/16-9.png'; +import ScreenshotArea from '../utils/screenshot-area'; + +export default function ButtonsScenario() { + return ( +
    +

    Preview

    + + null} + items={[ + { + type: 'icon-button', + id: 'download', + iconName: 'download', + text: 'Download', + }, + { + type: 'icon-button', + id: 'expand', + iconName: 'expand', + text: 'Expand', + }, + ]} + variant={'icon'} + /> + } + disableContentPaddings={true} + > + + + +
    + ); +} diff --git a/src/cards/styles.scss b/src/cards/styles.scss index a496a4e859..cf7fe5e785 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -75,6 +75,7 @@ .card-header-inner { /* Used in test utils */ + padding-block-start: 4px; } .card-header { diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index d8632843a2..84d4fa42c7 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -20,6 +20,7 @@ export default function Card({ onFocus, role, tagName: TagName = 'div', + disableContentPaddings, }: InternalCardProps) { const isRefresh = useVisualRefresh(); @@ -42,7 +43,7 @@ export default function Card({
    {header}
    {action &&
    {action}
    }
    - {children} +
    {children}
    ); diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index 159f8fc505..acb0e07da5 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -50,7 +50,9 @@ export interface InternalCardProps extends BaseComponentProps { tagName?: 'li' | 'div'; - metadataAttributes: Record; + disableContentPaddings?: boolean; - innerMetadataAttributes: Record; + metadataAttributes?: Record; + + innerMetadataAttributes?: Record; } diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index a40f933e82..20daf79c3d 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -72,13 +72,22 @@ } .header { - @include styles.font-heading-m; + @include styles.font-heading-s; + padding-block-start: 8px; + padding-inline: awsui.$space-card-horizontal; + display: flex; &-inner { + padding-block-start: 4px; inline-size: 100%; display: inline-block; } } +.body:not(.no-padding) { + padding-block-end: awsui.$space-card-vertical; + padding-inline: awsui.$space-card-horizontal; +} + .with-action { > .card-inner > .card-header { inline-size: 90%; @@ -94,3 +103,7 @@ } } } + +.action { + flex-shrink: 0; +} From f3c5c2948b6d040e3e9af83ed7a15ecd58c94349 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 09:40:18 +0100 Subject: [PATCH 06/33] Add description --- .../{preview.page.tsx => image-preview.page.tsx} | 15 ++++++++++++--- src/internal/components/card/index.tsx | 8 ++++++-- src/internal/components/card/interfaces.ts | 5 +++++ src/internal/components/card/styles.scss | 11 +++++++++-- 4 files changed, 32 insertions(+), 7 deletions(-) rename pages/card/{preview.page.tsx => image-preview.page.tsx} (77%) diff --git a/pages/card/preview.page.tsx b/pages/card/image-preview.page.tsx similarity index 77% rename from pages/card/preview.page.tsx rename to pages/card/image-preview.page.tsx index 493ca97b40..ed07f56045 100644 --- a/pages/card/preview.page.tsx +++ b/pages/card/image-preview.page.tsx @@ -11,11 +11,11 @@ import ScreenshotArea from '../utils/screenshot-area'; export default function ButtonsScenario() { return (
    -

    Preview

    +

    Image preview

    null} @@ -38,7 +38,16 @@ export default function ButtonsScenario() { } disableContentPaddings={true} > - +
    diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index 84d4fa42c7..b0bed78b35 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -14,6 +14,7 @@ export default function Card({ children, className, header, + description, innerMetadataAttributes, metadataAttributes, onClick, @@ -40,8 +41,11 @@ export default function Card({ onClick={onClick} >
    -
    {header}
    - {action &&
    {action}
    } +
    +
    {header}
    + {action &&
    {action}
    } +
    + {description &&
    {description}
    }
    {children}
    diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index acb0e07da5..6e3245b0bd 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -33,6 +33,11 @@ export interface InternalCardProps extends BaseComponentProps { */ header?: React.ReactNode; + /** + * Supplementary text below the heading. + */ + description?: React.ReactNode; + /** * Icon which will be displayed at the top of the card, * inline at the start of the content. diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 20daf79c3d..6d8f07b5d3 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -72,10 +72,13 @@ } .header { - @include styles.font-heading-s; padding-block-start: 8px; + padding-block-end: 4px; padding-inline: awsui.$space-card-horizontal; - display: flex; + &-top-row { + @include styles.font-heading-s; + display: flex; + } &-inner { padding-block-start: 4px; inline-size: 100%; @@ -107,3 +110,7 @@ .action { flex-shrink: 0; } + +.description { + color: awsui.$color-text-heading-secondary; +} From be87b4b3a504f169b9b2fd19c9b201f18303b2a5 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 09:44:07 +0100 Subject: [PATCH 07/33] Refactor prop name --- pages/card/image-preview.page.tsx | 2 +- src/cards/index.tsx | 2 +- src/internal/components/card/index.tsx | 6 +++--- src/internal/components/card/interfaces.ts | 2 +- src/internal/components/card/styles.scss | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pages/card/image-preview.page.tsx b/pages/card/image-preview.page.tsx index ed07f56045..3e2d19fbcb 100644 --- a/pages/card/image-preview.page.tsx +++ b/pages/card/image-preview.page.tsx @@ -16,7 +16,7 @@ export default function ButtonsScenario() { null} items={[ diff --git a/src/cards/index.tsx b/src/cards/index.tsx index 6ea70bbf35..b2f06b9ae7 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -316,7 +316,7 @@ const CardsList = ({ }; return (
    {header}
    - {action &&
    {action}
    } + {actions &&
    {actions}
    }
    {description &&
    {description}
    } diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index 6e3245b0bd..3253fb8287 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -9,7 +9,7 @@ export interface InternalCardProps extends BaseComponentProps { * Specifies an action for the card. * It is recommended to use a button with inline-icon variant. */ - action?: React.ReactNode; + actions?: React.ReactNode; /** * Specifies whether the card is in active state. diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 6d8f07b5d3..8815b4881d 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -91,7 +91,7 @@ padding-inline: awsui.$space-card-horizontal; } -.with-action { +.with-actions { > .card-inner > .card-header { inline-size: 90%; } @@ -107,7 +107,7 @@ } } -.action { +.actions { flex-shrink: 0; } From 5754d1451be3fbf8ba4b1f9ce87122f6c0189bc7 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 10:05:43 +0100 Subject: [PATCH 08/33] Add code snippet page --- pages/card/code-snippet.page.tsx | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 pages/card/code-snippet.page.tsx diff --git a/pages/card/code-snippet.page.tsx b/pages/card/code-snippet.page.tsx new file mode 100644 index 0000000000..0a6a0da67a --- /dev/null +++ b/pages/card/code-snippet.page.tsx @@ -0,0 +1,44 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Box from '~components/box'; +import CopyToClipboard from '~components/copy-to-clipboard'; +import Card from '~components/internal/components/card'; + +import ScreenshotArea from '../utils/screenshot-area'; + +export default function ButtonsScenario() { + return ( +
    +

    Code snippet

    + + + } + > + +
    +              {`def lambda_handler(event, context):
    +  bucket = event['Records'][0]['s3']['bucket']['name']
    +  key = event['Records'][0]['s3']['object']['key']
    +  print(f'New file uploaded: {key} in bucket {bucket}')
    +  return {'statusCode': 200}`}
    +            
    +
    +
    +
    +
    + ); +} From 50d376850c54c5b15312c7f7306d0f3045515df1 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 11:13:33 +0100 Subject: [PATCH 09/33] Let component prevent border overflow --- pages/card/image-preview.page.tsx | 2 -- src/internal/components/card/styles.scss | 13 ++++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pages/card/image-preview.page.tsx b/pages/card/image-preview.page.tsx index 3e2d19fbcb..318056832c 100644 --- a/pages/card/image-preview.page.tsx +++ b/pages/card/image-preview.page.tsx @@ -43,8 +43,6 @@ export default function ButtonsScenario() { width: '100%', height: '100%', display: 'block', - borderBottomLeftRadius: 16, - borderBottomRightRadius: 16, }} src={image} /> diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 8815b4881d..c1c1cdd5ea 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -86,9 +86,16 @@ } } -.body:not(.no-padding) { - padding-block-end: awsui.$space-card-vertical; - padding-inline: awsui.$space-card-horizontal; +.body { + &:not(.no-padding) { + padding-block-end: awsui.$space-card-vertical; + padding-inline: awsui.$space-card-horizontal; + } + &.no-padding { + border-end-start-radius: awsui.$border-radius-container; + border-end-end-radius: awsui.$border-radius-container; + overflow: scroll; + } } .with-actions { From c6cdf3727cffce17b03e563c47dbda49890f1271 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 11:15:50 +0100 Subject: [PATCH 10/33] Fine tune margin --- src/internal/components/card/styles.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index c1c1cdd5ea..a84c2f3968 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -120,4 +120,5 @@ .description { color: awsui.$color-text-heading-secondary; + margin-block-end: 8px; } From 4a9861238f1d40b2ab16abd29269e434119435b3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 11:20:27 +0100 Subject: [PATCH 11/33] Make dev pages narrower --- pages/card/code-snippet.page.tsx | 43 ++++++++++++++----------------- pages/card/common.tsx | 17 ++++++++++++ pages/card/image-preview.page.tsx | 7 +++-- 3 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 pages/card/common.tsx diff --git a/pages/card/code-snippet.page.tsx b/pages/card/code-snippet.page.tsx index 0a6a0da67a..317f45ad58 100644 --- a/pages/card/code-snippet.page.tsx +++ b/pages/card/code-snippet.page.tsx @@ -6,39 +6,36 @@ import Box from '~components/box'; import CopyToClipboard from '~components/copy-to-clipboard'; import Card from '~components/internal/components/card'; -import ScreenshotArea from '../utils/screenshot-area'; +import { CardPage } from './common'; export default function ButtonsScenario() { return ( -
    -

    Code snippet

    - - + - } - > - -
    -              {`def lambda_handler(event, context):
    +            copySuccessText="Code copied"
    +            copyErrorText="Error while copying text"
    +          />
    +        }
    +      >
    +        
    +          
    +            {`def lambda_handler(event, context):
       bucket = event['Records'][0]['s3']['bucket']['name']
       key = event['Records'][0]['s3']['object']['key']
       print(f'New file uploaded: {key} in bucket {bucket}')
       return {'statusCode': 200}`}
    -            
    -
    - - -
    + + +
    + ); } diff --git a/pages/card/common.tsx b/pages/card/common.tsx new file mode 100644 index 0000000000..2c935e2928 --- /dev/null +++ b/pages/card/common.tsx @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { ReactNode } from 'react'; +import React from 'react'; + +import ScreenshotArea from '../utils/screenshot-area'; + +export function CardPage({ title, children }: { title: string; children: ReactNode }) { + return ( +
    +

    {title}

    +
    + {children} +
    +
    + ); +} diff --git a/pages/card/image-preview.page.tsx b/pages/card/image-preview.page.tsx index 318056832c..0024baa770 100644 --- a/pages/card/image-preview.page.tsx +++ b/pages/card/image-preview.page.tsx @@ -6,13 +6,12 @@ import ButtonGroup from '~components/button-group'; import Card from '~components/internal/components/card'; import image from '../container/images/16-9.png'; -import ScreenshotArea from '../utils/screenshot-area'; +import { CardPage } from './common'; export default function ButtonsScenario() { return (
    -

    Image preview

    - + - +
    ); } From 347ed98942c9cab2d8be5651d1efd48fa521e86a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 12:08:55 +0100 Subject: [PATCH 12/33] Use maxWidth instead of width --- pages/card/common.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/card/common.tsx b/pages/card/common.tsx index 2c935e2928..2eff09bba8 100644 --- a/pages/card/common.tsx +++ b/pages/card/common.tsx @@ -9,7 +9,7 @@ export function CardPage({ title, children }: { title: string; children: ReactNo return (

    {title}

    -
    +
    {children}
    From dac7d5f1ac0dc272eebac98f13858a431c042180 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 20 Jan 2026 12:26:56 +0100 Subject: [PATCH 13/33] Refactor image preview page --- pages/card/image-preview.page.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pages/card/image-preview.page.tsx b/pages/card/image-preview.page.tsx index 0024baa770..02be9d3b4f 100644 --- a/pages/card/image-preview.page.tsx +++ b/pages/card/image-preview.page.tsx @@ -37,13 +37,14 @@ export default function ButtonsScenario() { } disableContentPaddings={true} > -
    From acbc6d1355adb4d41bb550ea3f04510fd6755cfc Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 21 Jan 2026 18:44:20 +0100 Subject: [PATCH 14/33] Add dev page with custom header --- .../image-preview-with-custom-header.page.tsx | 58 +++++++++++++++++++ src/internal/components/card/styles.scss | 19 +++--- 2 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 pages/card/image-preview-with-custom-header.page.tsx diff --git a/pages/card/image-preview-with-custom-header.page.tsx b/pages/card/image-preview-with-custom-header.page.tsx new file mode 100644 index 0000000000..7a5e476458 --- /dev/null +++ b/pages/card/image-preview-with-custom-header.page.tsx @@ -0,0 +1,58 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Box from '~components/box'; +import ButtonGroup from '~components/button-group'; +import Card from '~components/internal/components/card'; + +import image from '../container/images/16-9.png'; +import { CardPage } from './common'; + +export default function ButtonsScenario() { + return ( +
    + + +
    +
    image-title.jpg
    + null} + items={[ + { + type: 'icon-button', + id: 'download', + iconName: 'download', + text: 'Download', + }, + { + type: 'icon-button', + id: 'expand', + iconName: 'expand', + text: 'Expand', + }, + ]} + variant={'icon'} + /> +
    + Metadata about file - 4GB + + } + disableContentPaddings={true} + > +
    + + +
    + ); +} diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index a84c2f3968..6a6f392918 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -75,14 +75,9 @@ padding-block-start: 8px; padding-block-end: 4px; padding-inline: awsui.$space-card-horizontal; - &-top-row { - @include styles.font-heading-s; - display: flex; - } &-inner { - padding-block-start: 4px; - inline-size: 100%; - display: inline-block; + @include styles.font-heading-s; + min-block-size: 32px; } } @@ -98,9 +93,12 @@ } } -.with-actions { - > .card-inner > .card-header { - inline-size: 90%; +.with-actions > .card-inner > .header > .header-top-row { + display: flex; + > .header-inner { + inline-size: 100%; + display: flex; + align-items: center; } } @@ -120,5 +118,4 @@ .description { color: awsui.$color-text-heading-secondary; - margin-block-end: 8px; } From 5d1d6c1ccb560afd0ba4ba18a2bb8016cedc7db8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 21 Jan 2026 18:56:11 +0100 Subject: [PATCH 15/33] Adjust padding --- src/internal/components/card/styles.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 6a6f392918..3fbee2ce4c 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -74,7 +74,8 @@ .header { padding-block-start: 8px; padding-block-end: 4px; - padding-inline: awsui.$space-card-horizontal; + padding-inline-start: awsui.$space-card-horizontal; + padding-inline-end: calc(#{awsui.$space-card-horizontal} - 4px); &-inner { @include styles.font-heading-s; min-block-size: 32px; From 9ac939d11b56f0cf10e0606f810743d9d0d43ce1 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 22 Jan 2026 11:18:26 +0100 Subject: [PATCH 16/33] Use new header variant --- .../image-preview-with-small-header.page.tsx | 60 +++++++++++++++++++ src/header/interfaces.ts | 2 +- src/header/internal.tsx | 2 +- src/header/styles.scss | 18 +++++- 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 pages/card/image-preview-with-small-header.page.tsx diff --git a/pages/card/image-preview-with-small-header.page.tsx b/pages/card/image-preview-with-small-header.page.tsx new file mode 100644 index 0000000000..c704d81521 --- /dev/null +++ b/pages/card/image-preview-with-small-header.page.tsx @@ -0,0 +1,60 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import ButtonGroup from '~components/button-group'; +import Header from '~components/header'; +import Card from '~components/internal/components/card'; + +import image from '../container/images/16-9.png'; +import { CardPage } from './common'; + +export default function ButtonsScenario() { + return ( +
    + + null} + items={[ + { + type: 'icon-button', + id: 'download', + iconName: 'download', + text: 'Download', + }, + { + type: 'icon-button', + id: 'expand', + iconName: 'expand', + text: 'Expand', + }, + ]} + variant={'icon'} + /> + } + description="Metadata about file - 4GB" + > + image-title.jpg + + } + disableContentPaddings={true} + > +
    + + +
    + ); +} diff --git a/src/header/interfaces.ts b/src/header/interfaces.ts index 69204f02fd..f105ffb4e8 100644 --- a/src/header/interfaces.ts +++ b/src/header/interfaces.ts @@ -45,6 +45,6 @@ export interface HeaderProps extends BaseComponentProps { } export namespace HeaderProps { - export type Variant = 'h1' | 'h2' | 'h3' | 'awsui-h1-sticky'; + export type Variant = 'h1' | 'h2' | 'h3' | 'awsui-h1-sticky' | 'small'; export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5'; } diff --git a/src/header/internal.tsx b/src/header/internal.tsx index f9d6f3cd70..9d23cc43d4 100644 --- a/src/header/internal.tsx +++ b/src/header/internal.tsx @@ -37,7 +37,7 @@ export default function InternalHeader({ description, info, __internalRootRef, - __disableActionsWrapping, + __disableActionsWrapping = variant === 'small', __headingTagRef, __headingTagTabIndex, ...restProps diff --git a/src/header/styles.scss b/src/header/styles.scss index e7f2ba38a0..394d8eb672 100644 --- a/src/header/styles.scss +++ b/src/header/styles.scss @@ -19,8 +19,8 @@ flex-wrap: wrap; justify-content: space-between; - &.refresh, - &:not(.root-no-actions) { + &.refresh:not(.root-variant-small), + &:not(.root-no-actions):not(.root-variant-small) { row-gap: awsui.$space-scaled-xxs; } @@ -124,6 +124,10 @@ padding-inline: 0; } + &-variant-small { + flex-shrink: 0; + } + .root-has-description > .main > & { padding-block-end: 0; } @@ -159,6 +163,10 @@ &-variant-h3:not(.refresh) { padding-block-start: awsui.$space-scaled-xs; } + &-variant-small { + display: flex; + justify-content: space-between; + } .no-wrap > &:not(.refresh) { // Additional padding to make it the same height as a header with wrapping @@ -238,6 +246,12 @@ &-variant-h3 { @include styles.font(heading-m); } + &-variant-small { + display: flex; + align-items: center; + min-block-size: 32px; + @include styles.font-heading-s; + } } .heading-text { From 11896c019b7bfc7ae235dd41a31fa55c68227167 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 22 Jan 2026 11:49:47 +0100 Subject: [PATCH 17/33] Update documenter snapshots --- .../snapshot-tests/__snapshots__/documenter.test.ts.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index 8ee5e57f56..336b800fa9 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -13718,6 +13718,7 @@ use the \`id\` attribute, consider setting it on a parent element instead.", "name": "HeaderProps.Variant", "type": "union", "values": [ + "small", "h1", "h2", "h3", From e7d49da7a5c2d46d4d27317537a96025ad44c9b8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 26 Jan 2026 18:25:31 +0100 Subject: [PATCH 18/33] Spacing adjustments --- pages/card/image-preview-with-custom-header.page.tsx | 6 +++--- src/cards/styles.scss | 2 +- src/internal/components/card/styles.scss | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pages/card/image-preview-with-custom-header.page.tsx b/pages/card/image-preview-with-custom-header.page.tsx index 7a5e476458..90538e1d42 100644 --- a/pages/card/image-preview-with-custom-header.page.tsx +++ b/pages/card/image-preview-with-custom-header.page.tsx @@ -15,8 +15,8 @@ export default function ButtonsScenario() { -
    +
    +
    image-title.jpg
    null} @@ -38,7 +38,7 @@ export default function ButtonsScenario() { />
    Metadata about file - 4GB - +
    } disableContentPaddings={true} > diff --git a/src/cards/styles.scss b/src/cards/styles.scss index cf7fe5e785..d99528c5f6 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -75,11 +75,11 @@ .card-header-inner { /* Used in test utils */ - padding-block-start: 4px; } .card-header { @include styles.font-heading-m; + padding-block-start: 8px; } .section { diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 3fbee2ce4c..7d8acdafc5 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -64,8 +64,8 @@ background-color: awsui.$color-background-container-content; margin-block: 0; margin-inline: 0; - padding-block: awsui.$space-card-vertical; - padding-inline: awsui.$space-card-horizontal; + padding-block: 0; + padding-inline: 0; inline-size: 100%; min-inline-size: 0; @include card-style; @@ -73,12 +73,10 @@ .header { padding-block-start: 8px; - padding-block-end: 4px; padding-inline-start: awsui.$space-card-horizontal; padding-inline-end: calc(#{awsui.$space-card-horizontal} - 4px); &-inner { @include styles.font-heading-s; - min-block-size: 32px; } } @@ -96,8 +94,10 @@ .with-actions > .card-inner > .header > .header-top-row { display: flex; + inline-size: 100%; + justify-content: space-between; > .header-inner { - inline-size: 100%; + min-block-size: 32px; display: flex; align-items: center; } @@ -119,4 +119,5 @@ .description { color: awsui.$color-text-heading-secondary; + margin-block-end: 4px; } From 01d2e4a5280c664f6ce8751610bdbceb296824cf Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 28 Jan 2026 17:31:48 +0100 Subject: [PATCH 19/33] Refactor --- src/cards/index.tsx | 115 +++++++++++---------- src/cards/styles.scss | 24 +++-- src/internal/components/card/index.tsx | 42 ++++---- src/internal/components/card/interfaces.ts | 10 +- src/internal/components/card/styles.scss | 25 +---- 5 files changed, 100 insertions(+), 116 deletions(-) diff --git a/src/cards/index.tsx b/src/cards/index.tsx index b2f06b9ae7..9b4dd99be2 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -315,65 +315,68 @@ const CardsList = ({ }, }; return ( - - -
    - ) - } - active={selectable && selected} - className={styles.card} - header={ - cardDefinition.header ? ( -
    - {cardDefinition.header(item)} -
    - ) : ( - '' - ) - } - innerMetadataAttributes={ - canClickEntireCard && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {} - } - key={index} - metadataAttributes={{ - ...getAnalyticsMetadataAttribute({ - component: { - innerContext: { - position: `${index + 1}`, - item: `${key}`, - }, - }, - }), - ...focusMarkers.item, - }} - onClick={ - canClickEntireCard - ? event => { - selectionProps?.onChange(); - // Manually move focus to the native input (checkbox or radio button) - event.currentTarget.querySelector('input')?.focus(); - } - : undefined - } +
  • - {visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => ( -
    - {header ?
    {header}
    : ''} - {content ?
    {content(item)}
    : ''} -
    - ))} - + + + + ) + } + active={selectable && selected} + header={ + cardDefinition.header ? ( +
    + {cardDefinition.header(item)} +
    + ) : ( + '' + ) + } + metadataAttributes={ + canClickEntireCard && !disabled ? getAnalyticsMetadataAttribute(selectionAnalyticsMetadata) : {} + } + onClick={ + canClickEntireCard + ? event => { + selectionProps?.onChange(); + // Manually move focus to the native input (checkbox or radio button) + event.currentTarget.querySelector('input')?.focus(); + } + : undefined + } + > + {visibleSectionsDefinition.map(({ width = 100, header, content, id }, index) => ( +
    + {header ?
    {header}
    : ''} + {content ?
    {content(item)}
    : ''} +
    + ))} +
    +
  • ); })} diff --git a/src/cards/styles.scss b/src/cards/styles.scss index d99528c5f6..96244ff3d2 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -73,13 +73,23 @@ } } -.card-header-inner { - /* Used in test utils */ -} - -.card-header { - @include styles.font-heading-m; - padding-block-start: 8px; +.card { + display: flex; + overflow-wrap: break-word; + word-wrap: break-word; + box-sizing: border-box; + padding-block-start: 0; + padding-block-end: awsui.$space-grid-gutter; + padding-inline-start: awsui.$space-grid-gutter; + padding-inline-end: 0; + list-style: none; + &-header { + @include styles.font-heading-m; + padding-block-start: 8px; + &-inner { + /* Used in test utils */ + } + } } .section { diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index 723da8c3c8..fd9fadf965 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -15,40 +15,34 @@ export default function Card({ className, header, description, - innerMetadataAttributes, metadataAttributes, onClick, - onFocus, - role, - tagName: TagName = 'div', disableContentPaddings, }: InternalCardProps) { const isRefresh = useVisualRefresh(); return ( - -
    -
    -
    -
    {header}
    - {actions &&
    {actions}
    } -
    - {description &&
    {description}
    } +
    +
    +
    {header}
    + {actions &&
    {actions}
    }
    -
    {children}
    + {description &&
    {description}
    }
    - +
    {children}
    +
    ); } diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index 3253fb8287..a91bd41534 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import React, { FocusEventHandler } from 'react'; +import React from 'react'; import { BaseComponentProps } from '../../base-component'; @@ -49,15 +49,7 @@ export interface InternalCardProps extends BaseComponentProps { */ onClick?: React.MouseEventHandler; - onFocus?: FocusEventHandler; - - role?: string; - - tagName?: 'li' | 'div'; - disableContentPaddings?: boolean; metadataAttributes?: Record; - - innerMetadataAttributes?: Record; } diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 7d8acdafc5..70459e91f3 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -46,20 +46,7 @@ .root { @include styles.styles-reset(); - display: flex; - overflow-wrap: break-word; - word-wrap: break-word; box-sizing: border-box; - margin-block: 0; - margin-inline: 0; - padding-block-start: 0; - padding-block-end: awsui.$space-grid-gutter; - padding-inline-start: awsui.$space-grid-gutter; - padding-inline-end: 0; - list-style: none; -} - -.card-inner { position: relative; background-color: awsui.$color-background-container-content; margin-block: 0; @@ -92,7 +79,7 @@ } } -.with-actions > .card-inner > .header > .header-top-row { +.with-actions > .header > .header-top-row { display: flex; inline-size: 100%; justify-content: space-between; @@ -104,12 +91,10 @@ } .active { - > .card-inner { - background-color: awsui.$color-background-item-selected; - &::before { - border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; - border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; - } + background-color: awsui.$color-background-item-selected; + &::before { + border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; + border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; } } From 93781b1ea5194a0a53a2f940c0db1477c1fc8538 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 28 Jan 2026 17:55:25 +0100 Subject: [PATCH 20/33] Revert "Use new header variant" This reverts commit 9ac939d11b56f0cf10e0606f810743d9d0d43ce1. --- .../image-preview-with-small-header.page.tsx | 60 ------------------- src/header/interfaces.ts | 2 +- src/header/internal.tsx | 2 +- src/header/styles.scss | 18 +----- 4 files changed, 4 insertions(+), 78 deletions(-) delete mode 100644 pages/card/image-preview-with-small-header.page.tsx diff --git a/pages/card/image-preview-with-small-header.page.tsx b/pages/card/image-preview-with-small-header.page.tsx deleted file mode 100644 index c704d81521..0000000000 --- a/pages/card/image-preview-with-small-header.page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import React from 'react'; - -import ButtonGroup from '~components/button-group'; -import Header from '~components/header'; -import Card from '~components/internal/components/card'; - -import image from '../container/images/16-9.png'; -import { CardPage } from './common'; - -export default function ButtonsScenario() { - return ( -
    - - null} - items={[ - { - type: 'icon-button', - id: 'download', - iconName: 'download', - text: 'Download', - }, - { - type: 'icon-button', - id: 'expand', - iconName: 'expand', - text: 'Expand', - }, - ]} - variant={'icon'} - /> - } - description="Metadata about file - 4GB" - > - image-title.jpg - - } - disableContentPaddings={true} - > -
    - - -
    - ); -} diff --git a/src/header/interfaces.ts b/src/header/interfaces.ts index f105ffb4e8..69204f02fd 100644 --- a/src/header/interfaces.ts +++ b/src/header/interfaces.ts @@ -45,6 +45,6 @@ export interface HeaderProps extends BaseComponentProps { } export namespace HeaderProps { - export type Variant = 'h1' | 'h2' | 'h3' | 'awsui-h1-sticky' | 'small'; + export type Variant = 'h1' | 'h2' | 'h3' | 'awsui-h1-sticky'; export type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5'; } diff --git a/src/header/internal.tsx b/src/header/internal.tsx index 9d23cc43d4..f9d6f3cd70 100644 --- a/src/header/internal.tsx +++ b/src/header/internal.tsx @@ -37,7 +37,7 @@ export default function InternalHeader({ description, info, __internalRootRef, - __disableActionsWrapping = variant === 'small', + __disableActionsWrapping, __headingTagRef, __headingTagTabIndex, ...restProps diff --git a/src/header/styles.scss b/src/header/styles.scss index 394d8eb672..e7f2ba38a0 100644 --- a/src/header/styles.scss +++ b/src/header/styles.scss @@ -19,8 +19,8 @@ flex-wrap: wrap; justify-content: space-between; - &.refresh:not(.root-variant-small), - &:not(.root-no-actions):not(.root-variant-small) { + &.refresh, + &:not(.root-no-actions) { row-gap: awsui.$space-scaled-xxs; } @@ -124,10 +124,6 @@ padding-inline: 0; } - &-variant-small { - flex-shrink: 0; - } - .root-has-description > .main > & { padding-block-end: 0; } @@ -163,10 +159,6 @@ &-variant-h3:not(.refresh) { padding-block-start: awsui.$space-scaled-xs; } - &-variant-small { - display: flex; - justify-content: space-between; - } .no-wrap > &:not(.refresh) { // Additional padding to make it the same height as a header with wrapping @@ -246,12 +238,6 @@ &-variant-h3 { @include styles.font(heading-m); } - &-variant-small { - display: flex; - align-items: center; - min-block-size: 32px; - @include styles.font-heading-s; - } } .heading-text { From d635b0a0cc531c92ad2dd695ec908dbc4681ddd2 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 28 Jan 2026 17:58:01 +0100 Subject: [PATCH 21/33] Updat documenter snapshots --- .../snapshot-tests/__snapshots__/documenter.test.ts.snap | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap index 336b800fa9..8ee5e57f56 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap @@ -13718,7 +13718,6 @@ use the \`id\` attribute, consider setting it on a parent element instead.", "name": "HeaderProps.Variant", "type": "union", "values": [ - "small", "h1", "h2", "h3", From 6282259da67d2e51ae2c30dbbca583773274e761 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 28 Jan 2026 18:06:23 +0100 Subject: [PATCH 22/33] Restore selector --- src/cards/index.tsx | 1 - src/cards/styles.scss | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cards/index.tsx b/src/cards/index.tsx index 9b4dd99be2..aa7358234e 100644 --- a/src/cards/index.tsx +++ b/src/cards/index.tsx @@ -317,7 +317,6 @@ const CardsList = ({ return (
  • Date: Wed, 28 Jan 2026 18:11:47 +0100 Subject: [PATCH 23/33] Spacing fixes --- src/cards/styles.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cards/styles.scss b/src/cards/styles.scss index df8d1f13e8..b25a645ebc 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -78,6 +78,8 @@ overflow-wrap: break-word; word-wrap: break-word; box-sizing: border-box; + margin-block: 0; + margin-inline: 0; padding-block-start: 0; padding-block-end: awsui.$space-grid-gutter; padding-inline-start: awsui.$space-grid-gutter; @@ -88,7 +90,6 @@ } &-header { @include styles.font-heading-m; - padding-block-start: 8px; &-inner { /* Used in test utils */ } From 4bb564209d567323a0b94d6acc3ba13ad160628f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 28 Jan 2026 18:22:28 +0100 Subject: [PATCH 24/33] Fix selectors --- .../__snapshots__/test-utils-selectors.test.tsx.snap | 3 +-- src/cards/styles.scss | 6 +++--- src/test-utils/dom/cards/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap index ce643df1e7..c5114c6806 100644 --- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap +++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap @@ -117,6 +117,7 @@ exports[`test-utils selectors 1`] = ` ], "cards": [ "awsui_card-header-inner_p8a6i", + "awsui_card-selected_p8a6i", "awsui_card_p8a6i", "awsui_empty_p8a6i", "awsui_loading_p8a6i", @@ -356,7 +357,6 @@ exports[`test-utils selectors 1`] = ` ], "internal": [ "awsui_actions_x6dl3", - "awsui_active_1xxz5", "awsui_application_1fcus", "awsui_axis-label--x_f0fot", "awsui_axis-label--y_f0fot", @@ -406,7 +406,6 @@ exports[`test-utils selectors 1`] = ` "awsui_root_1om0h", "awsui_root_1qprf", "awsui_root_1t44z", - "awsui_root_1xxz5", "awsui_root_qwoo0", "awsui_root_vrgzu", "awsui_screenreader-content_15o6u", diff --git a/src/cards/styles.scss b/src/cards/styles.scss index b25a645ebc..c78d58cc7c 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -85,15 +85,15 @@ padding-inline-start: awsui.$space-grid-gutter; padding-inline-end: 0; list-style: none; - &.selected { - /* Used in test utils */ - } &-header { @include styles.font-heading-m; &-inner { /* Used in test utils */ } } + &-selected { + /* Used in test utils */ + } } .section { diff --git a/src/test-utils/dom/cards/index.ts b/src/test-utils/dom/cards/index.ts index 574a6f7250..0c83836402 100644 --- a/src/test-utils/dom/cards/index.ts +++ b/src/test-utils/dom/cards/index.ts @@ -46,11 +46,11 @@ export default class CardsWrapper extends ComponentWrapper { private containerWrapper = new ContainerWrapper(this.getElement()); findItems(): Array { - return this.findAll(`:is(.${cardStyles.root}, .${styles.card})`).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(styles.card).map(c => new CardWrapper(c.getElement())); } findSelectedItems(): Array { - return this.findAllByClassName(cardStyles.active).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(styles['card-selected']).map(c => new CardWrapper(c.getElement())); } findHeader(): ElementWrapper | null { From d8c2aca817c8786d321069e33b0db6cf64672953 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 3 Dec 2025 10:11:17 +0100 Subject: [PATCH 25/33] chore: Refactor card into separate internal component --- src/test-utils/dom/cards/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-utils/dom/cards/index.ts b/src/test-utils/dom/cards/index.ts index 0c83836402..1fb1ecd10f 100644 --- a/src/test-utils/dom/cards/index.ts +++ b/src/test-utils/dom/cards/index.ts @@ -50,7 +50,7 @@ export default class CardsWrapper extends ComponentWrapper { } findSelectedItems(): Array { - return this.findAllByClassName(styles['card-selected']).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(cardStyles['card-active']).map(c => new CardWrapper(c.getElement())); } findHeader(): ElementWrapper | null { From f07fd16c4e3a5e4caa11bf6892df7ee85b64e494 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 3 Dec 2025 18:14:36 +0100 Subject: [PATCH 26/33] Minor refactor --- src/test-utils/dom/cards/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test-utils/dom/cards/index.ts b/src/test-utils/dom/cards/index.ts index 1fb1ecd10f..b7f72352b8 100644 --- a/src/test-utils/dom/cards/index.ts +++ b/src/test-utils/dom/cards/index.ts @@ -46,11 +46,11 @@ export default class CardsWrapper extends ComponentWrapper { private containerWrapper = new ContainerWrapper(this.getElement()); findItems(): Array { - return this.findAllByClassName(styles.card).map(c => new CardWrapper(c.getElement())); + return this.findAll(styles.card).map(c => new CardWrapper(c.getElement())); } findSelectedItems(): Array { - return this.findAllByClassName(cardStyles['card-active']).map(c => new CardWrapper(c.getElement())); + return this.findAllByClassName(styles['card-selected']).map(c => new CardWrapper(c.getElement())); } findHeader(): ElementWrapper | null { From 703d23ce683fd8a7038e69b9daa49e30f4c4bc9b Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 15 Jan 2026 12:26:14 +0100 Subject: [PATCH 27/33] Add preview example --- pages/card/preview.page.tsx | 46 +++++++++++++++++++++++++++++++++++++ src/cards/styles.scss | 1 + 2 files changed, 47 insertions(+) create mode 100644 pages/card/preview.page.tsx diff --git a/pages/card/preview.page.tsx b/pages/card/preview.page.tsx new file mode 100644 index 0000000000..493ca97b40 --- /dev/null +++ b/pages/card/preview.page.tsx @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import ButtonGroup from '~components/button-group'; +import Card from '~components/internal/components/card'; + +import image from '../container/images/16-9.png'; +import ScreenshotArea from '../utils/screenshot-area'; + +export default function ButtonsScenario() { + return ( +
    +

    Preview

    + + null} + items={[ + { + type: 'icon-button', + id: 'download', + iconName: 'download', + text: 'Download', + }, + { + type: 'icon-button', + id: 'expand', + iconName: 'expand', + text: 'Expand', + }, + ]} + variant={'icon'} + /> + } + disableContentPaddings={true} + > + + + +
    + ); +} diff --git a/src/cards/styles.scss b/src/cards/styles.scss index c78d58cc7c..92a34898c7 100644 --- a/src/cards/styles.scss +++ b/src/cards/styles.scss @@ -89,6 +89,7 @@ @include styles.font-heading-m; &-inner { /* Used in test utils */ + padding-block-start: 4px; } } &-selected { From 0b712f1c8d60e57fc9c50c098e705ec2565723ef Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 22 Jan 2026 20:39:25 +0100 Subject: [PATCH 28/33] Refactor --- src/internal/components/card/styles.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index 70459e91f3..fd75a88b95 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -67,6 +67,13 @@ } } +.header-inner, +.actions { + display: flex; + align-items: center; + min-block-size: 32px; +} + .body { &:not(.no-padding) { padding-block-end: awsui.$space-card-vertical; From 8e1c7f6c27bd4ffa3e9a124868f1a9f3a5293f6e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 22 Jan 2026 20:41:50 +0100 Subject: [PATCH 29/33] Fix page --- pages/card/action-card.page.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 pages/card/action-card.page.tsx diff --git a/pages/card/action-card.page.tsx b/pages/card/action-card.page.tsx new file mode 100644 index 0000000000..d688b3d208 --- /dev/null +++ b/pages/card/action-card.page.tsx @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + +import Card from '~components/internal/components/card'; + +import { CardPage } from './common'; + +export default function ButtonsScenario() { + return ( + + + A description of the template / ice breaker + + + ); +} From 7d19d7675e2bd823fd91a7502dba5352bbc62cf9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 23 Jan 2026 13:45:30 +0100 Subject: [PATCH 30/33] Action card --- pages/card/action-card-in-chat.page.tsx | 18 ++++++ pages/card/action-card-in-list.page.tsx | 75 ++++++++++++++++++++++ pages/card/action-card.page.tsx | 17 ----- src/internal/components/card/index.tsx | 10 ++- src/internal/components/card/interfaces.ts | 2 + src/internal/components/card/styles.scss | 68 ++++++++++++++++---- 6 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 pages/card/action-card-in-chat.page.tsx create mode 100644 pages/card/action-card-in-list.page.tsx delete mode 100644 pages/card/action-card.page.tsx diff --git a/pages/card/action-card-in-chat.page.tsx b/pages/card/action-card-in-chat.page.tsx new file mode 100644 index 0000000000..6e156d5e3d --- /dev/null +++ b/pages/card/action-card-in-chat.page.tsx @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Card from '~components/internal/components/card'; + +import { CardPage } from './common'; + +export default function ButtonsScenario() { + const [isActive, setActive] = useState(false); + return ( + + setActive(true)}> + A more detailed description of the action. + + + ); +} diff --git a/pages/card/action-card-in-list.page.tsx b/pages/card/action-card-in-list.page.tsx new file mode 100644 index 0000000000..83f31969cf --- /dev/null +++ b/pages/card/action-card-in-list.page.tsx @@ -0,0 +1,75 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; + +import Box from '~components/box'; +import Container from '~components/container'; +import Header from '~components/header'; +import Card from '~components/internal/components/card'; + +import ScreenshotArea from '../utils/screenshot-area'; + +const accounts = [ + { + alias: 'Account alias', + id: '873423479685', + role: 'Dev', + email: 'john.doe@anycompany.com', + lastLogin: '1 minute ago', + }, + { + id: '63547903567', + role: 'ReadOnly', + email: 'john.doe@anycompany.com', + lastLogin: '10 minute ago', + }, + { + id: '583821526507', + role: 'Root', + lastLogin: '2 hours ago', + }, + { + alias: 'acme-staging-infra', + id: '886694904548', + role: 'Admin', + email: 'john.doe@anycompany.com', + lastLogin: '3 hours ago', + }, + { + alias: 'acme-prod-monitoring', + id: '634308714948', + role: 'PowerUser', + email: 'john.doe@anycompany.com', + lastLogin: '10 hours ago', + }, +]; + +export default function ButtonsScenario() { + const [activeId, setActiveId] = useState(); + return ( +
    +

    Action card: list selection

    + +
    + Choose an active session}> +
      + {accounts.map(({ alias, id, role, email, lastLogin }) => ( + setActiveId(id)} + > + {`Logged in ${lastLogin}`} + + ))} +
    +
    +
    +
    +
    + ); +} diff --git a/pages/card/action-card.page.tsx b/pages/card/action-card.page.tsx deleted file mode 100644 index d688b3d208..0000000000 --- a/pages/card/action-card.page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import React from 'react'; - -import Card from '~components/internal/components/card'; - -import { CardPage } from './common'; - -export default function ButtonsScenario() { - return ( - - - A description of the template / ice breaker - - - ); -} diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index fd9fadf965..73fa5ef9ba 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -3,6 +3,7 @@ import React from 'react'; import clsx from 'clsx'; +import InternalIcon from '../../../icon/internal'; import { useVisualRefresh } from '../../hooks/use-visual-mode'; import { InternalCardProps } from './interfaces'; @@ -18,9 +19,12 @@ export default function Card({ metadataAttributes, onClick, disableContentPaddings, + variant = 'default', }: InternalCardProps) { const isRefresh = useVisualRefresh(); + const hasActions = !!actions || variant === 'action'; + return (
    {header}
    - {actions &&
    {actions}
    } + {hasActions && ( +
    +
    {actions || }
    +
    + )}
    {description &&
    {description}
    }
    diff --git a/src/internal/components/card/interfaces.ts b/src/internal/components/card/interfaces.ts index a91bd41534..62b50b6831 100644 --- a/src/internal/components/card/interfaces.ts +++ b/src/internal/components/card/interfaces.ts @@ -52,4 +52,6 @@ export interface InternalCardProps extends BaseComponentProps { disableContentPaddings?: boolean; metadataAttributes?: Record; + + variant?: 'action' | 'default'; } diff --git a/src/internal/components/card/styles.scss b/src/internal/components/card/styles.scss index fd75a88b95..d234ff50df 100644 --- a/src/internal/components/card/styles.scss +++ b/src/internal/components/card/styles.scss @@ -5,17 +5,26 @@ @use 'sass:math'; +@use '@cloudscape-design/component-toolkit/internal/focus-visible' as focus-visible; @use '../../styles' as styles; @use '../../styles/tokens' as awsui; @use './motion'; @mixin card-style { + @include styles.styles-reset(); + border-start-start-radius: awsui.$border-radius-container; border-start-end-radius: awsui.$border-radius-container; border-end-start-radius: awsui.$border-radius-container; border-end-end-radius: awsui.$border-radius-container; box-sizing: border-box; + // Override button styles for action cards + padding-inline: 0; + border-block: 0 none; + border-inline: 0 none; + user-select: text; + &::before { @include styles.base-pseudo-element; // Reset border color to prevent it from flashing black during card selection animation @@ -39,8 +48,11 @@ box-shadow: awsui.$shadow-container; } &.refresh::after { - border-block: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; - border-inline: solid awsui.$border-divider-section-width awsui.$color-border-divider-default; + border-block: solid awsui.$border-divider-section-width; + border-inline: solid awsui.$border-divider-section-width; + } + &:not(.variant-action)::after { + border-color: awsui.$color-border-divider-default; } } @@ -48,7 +60,7 @@ @include styles.styles-reset(); box-sizing: border-box; position: relative; - background-color: awsui.$color-background-container-content; + background-color: transparent; margin-block: 0; margin-inline: 0; padding-block: 0; @@ -56,6 +68,48 @@ inline-size: 100%; min-inline-size: 0; @include card-style; + /* stylelint-disable-next-line selector-combinator-disallowed-list */ + &:not(.active):not(:hover) .actions-inner { + color: awsui.$color-border-button-normal-default; + } + &.active { + &::before { + border-block: awsui.$border-item-width solid; + border-inline: awsui.$border-item-width solid; + } + &:not(.variant-action) { + background-color: awsui.$color-background-item-selected; + &::before { + border-color: awsui.$color-border-item-selected; + } + } + &.variant-action::before { + border-color: awsui.$color-border-button-normal-hover; + } + } + &.variant-action { + cursor: pointer; + + &:not(:hover) { + &::after { + border-color: awsui.$color-border-button-normal-default; + } + } + &:hover::after { + border-color: awsui.$color-border-button-normal-hover; + } + @include focus-visible.when-visible { + &::before { + border-block-width: 1px; + border-inline-width: 1px; + } + @include styles.focus-highlight( + $gutter: 0, + $border-radius: awsui.$border-radius-container, + $box-shadow: styles.$box-shadow-focused-light + ); + } + } } .header { @@ -97,14 +151,6 @@ } } -.active { - background-color: awsui.$color-background-item-selected; - &::before { - border-block: awsui.$border-item-width solid awsui.$color-border-item-selected; - border-inline: awsui.$border-item-width solid awsui.$color-border-item-selected; - } -} - .actions { flex-shrink: 0; } From d1b377ad7e1b32f37b0f8dafebd37c213c83fe50 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 23 Jan 2026 16:35:41 +0100 Subject: [PATCH 31/33] Fix code snippet dark mode background color --- pages/card/code-snippet.page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/card/code-snippet.page.tsx b/pages/card/code-snippet.page.tsx index 317f45ad58..94115362cb 100644 --- a/pages/card/code-snippet.page.tsx +++ b/pages/card/code-snippet.page.tsx @@ -27,7 +27,7 @@ return {'statusCode': 200}" } > -
    +          
                 {`def lambda_handler(event, context):
       bucket = event['Records'][0]['s3']['bucket']['name']
       key = event['Records'][0]['s3']['object']['key']
    
    From a282780f4e7744f17f88929ce6b18538c6ba9f9b Mon Sep 17 00:00:00 2001
    From: Joan Perals Tresserra 
    Date: Fri, 23 Jan 2026 17:00:52 +0100
    Subject: [PATCH 32/33] Add ARIA label to copy button
    
    ---
     pages/card/code-snippet.page.tsx | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/pages/card/code-snippet.page.tsx b/pages/card/code-snippet.page.tsx
    index 94115362cb..f9014cac03 100644
    --- a/pages/card/code-snippet.page.tsx
    +++ b/pages/card/code-snippet.page.tsx
    @@ -23,6 +23,7 @@ print(f'New file uploaded: {key} in bucket {bucket}')
     return {'statusCode': 200}"
                 copySuccessText="Code copied"
                 copyErrorText="Error while copying text"
    +            copyButtonAriaLabel="Copy code"
               />
             }
           >
    
    From 33cd75e72914fd6d3f0bef7a1026158c2d8d0689 Mon Sep 17 00:00:00 2001
    From: Joan Perals Tresserra 
    Date: Thu, 29 Jan 2026 10:02:49 +0100
    Subject: [PATCH 33/33] Post-rebase fixes
    
    ---
     pages/card/action-card-in-list.page.tsx | 23 ++++++++++++-----------
     pages/card/preview.page.tsx             |  4 ++--
     src/internal/components/card/index.tsx  |  3 ++-
     3 files changed, 16 insertions(+), 14 deletions(-)
    
    diff --git a/pages/card/action-card-in-list.page.tsx b/pages/card/action-card-in-list.page.tsx
    index 83f31969cf..8a6c6b85e2 100644
    --- a/pages/card/action-card-in-list.page.tsx
    +++ b/pages/card/action-card-in-list.page.tsx
    @@ -54,17 +54,18 @@ export default function ButtonsScenario() {
               Choose an active session}>
                 
      {accounts.map(({ alias, id, role, email, lastLogin }) => ( - setActiveId(id)} - > - {`Logged in ${lastLogin}`} - +
    1. + setActiveId(id)} + > + {`Logged in ${lastLogin}`} + +
    2. ))}
    diff --git a/pages/card/preview.page.tsx b/pages/card/preview.page.tsx index 493ca97b40..ba4977f894 100644 --- a/pages/card/preview.page.tsx +++ b/pages/card/preview.page.tsx @@ -15,8 +15,8 @@ export default function ButtonsScenario() { null} items={[ diff --git a/src/internal/components/card/index.tsx b/src/internal/components/card/index.tsx index 73fa5ef9ba..70f370889c 100644 --- a/src/internal/components/card/index.tsx +++ b/src/internal/components/card/index.tsx @@ -30,8 +30,9 @@ export default function Card({ className={clsx( className, styles.root, + styles[`variant-${variant}`], { - [styles['with-actions']]: !!actions, + [styles['with-actions']]: hasActions, [styles.active]: active, }, isRefresh && styles.refresh