diff --git a/change/@fluentui-react-card-e65cb71c-d483-449c-b1cb-b6b23d07f25b.json b/change/@fluentui-react-card-e65cb71c-d483-449c-b1cb-b6b23d07f25b.json
new file mode 100644
index 0000000000000..0a54204a51cf7
--- /dev/null
+++ b/change/@fluentui-react-card-e65cb71c-d483-449c-b1cb-b6b23d07f25b.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "fix: infer a11y id from immediate header element",
+ "packageName": "@fluentui/react-card",
+ "email": "marcosvmmoura@gmail.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/react-components/react-card/src/components/Card/Card.cy.tsx b/packages/react-components/react-card/src/components/Card/Card.cy.tsx
index c664a14329213..99bc6667ad560 100644
--- a/packages/react-components/react-card/src/components/Card/Card.cy.tsx
+++ b/packages/react-components/react-card/src/components/Card/Card.cy.tsx
@@ -60,6 +60,29 @@ const CardSample = (props: CardProps) => (
>
);
+const CardWithCustomHeader = ({
+ customHeaderId = 'custom-header-id',
+ ...props
+}: CardProps & { customHeaderId: string }) => (
+ <>
+
+ Before
+
+
+
+ }
+ header={}
+ description={Developer}
+ />
+
+
+
+ After
+
+ >
+);
+
const CardWithPreview = (props: CardProps) => (
<>
@@ -423,6 +446,17 @@ describe('Card', () => {
});
});
+ it('should sync selectable aria-labelledby with card header immediate child', () => {
+ const customHeaderId = 'custom-header';
+
+ mountFluent();
+
+ cy.get(`.${cardHeaderClassNames.header}`).should('not.have.attr', 'id');
+ cy.get(`.${cardClassNames.checkbox}`).then(slot => {
+ cy.get(`#${customHeaderId}`).then(() => expect(customHeaderId).equals(slot.attr('aria-labelledby')));
+ });
+ });
+
it('should sync selectable aria-label with card preview alt', () => {
mountFluent();
diff --git a/packages/react-components/react-card/src/components/CardHeader/useCardHeader.ts b/packages/react-components/react-card/src/components/CardHeader/useCardHeader.ts
index 3f1f487923d6c..b74a0634fe39c 100644
--- a/packages/react-components/react-card/src/components/CardHeader/useCardHeader.ts
+++ b/packages/react-components/react-card/src/components/CardHeader/useCardHeader.ts
@@ -4,6 +4,44 @@ import type { CardHeaderProps, CardHeaderState } from './CardHeader.types';
import { useCardContext_unstable } from '../Card/CardContext';
import { cardHeaderClassNames } from './useCardHeaderStyles.styles';
+/**
+ * Finds the first child of CardHeader with an id prop.
+ *
+ * @param header - the header prop of CardHeader
+ */
+function getChildWithId(header: CardHeaderProps['header']) {
+ function isReactElementWithIdProp(element: React.ReactNode): element is React.ReactElement {
+ return React.isValidElement(element) && Boolean(element.props.id);
+ }
+
+ return React.Children.toArray(header).find(isReactElementWithIdProp);
+}
+
+/**
+ * Returns the id to use for the CardHeader root element.
+ *
+ * @param headerId - the id prop of the CardHeader component
+ * @param childWithId - the first child of the CardHeader component with an id prop
+ * @param generatedId - a generated id
+ *
+ * @returns the id to use for the CardHeader root element
+ */
+function getReferenceId(
+ headerId: string | undefined,
+ childWithId: React.ReactElement | undefined,
+ generatedId: string,
+): string {
+ if (headerId) {
+ return headerId;
+ }
+
+ if (childWithId?.props.id) {
+ return childWithId.props.id;
+ }
+
+ return generatedId;
+}
+
/**
* Create the state required to render CardHeader.
*
@@ -21,15 +59,17 @@ export const useCardHeader_unstable = (props: CardHeaderProps, ref: React.Ref(null);
+ const hasChildId = React.useRef(false);
const generatedId = useId(cardHeaderClassNames.header, referenceId);
React.useEffect(() => {
- if (header && headerRef.current) {
- const { id } = headerRef.current;
+ const headerId = !hasChildId.current ? headerRef.current?.id : undefined;
+ const childWithId = getChildWithId(header);
+
+ hasChildId.current = Boolean(childWithId);
- setReferenceId(id ? id : generatedId);
- }
- }, [header, setReferenceId, generatedId]);
+ setReferenceId(getReferenceId(headerId, childWithId, generatedId));
+ }, [generatedId, header, setReferenceId]);
return {
components: {
@@ -49,7 +89,7 @@ export const useCardHeader_unstable = (props: CardHeaderProps, ref: React.Ref