Add alt text support to card covers (#3715)

This commit is contained in:
Nolann B.
2025-10-24 16:27:23 +02:00
committed by GitHub
parent 63bf4dbb16
commit f9f80117f0
7 changed files with 38 additions and 23 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@gitbook/react-openapi': patch
'gitbook': patch
---
Add alt text support to card covers
+2 -2
View File
@@ -343,7 +343,7 @@
"react-dom": "catalog:", "react-dom": "catalog:",
}, },
"catalog": { "catalog": {
"@gitbook/api": "0.145.0", "@gitbook/api": "0.146.0",
"@scalar/api-client-react": "^1.3.46", "@scalar/api-client-react": "^1.3.46",
"@tsconfig/node20": "^20.1.6", "@tsconfig/node20": "^20.1.6",
"@tsconfig/strictest": "^2.0.6", "@tsconfig/strictest": "^2.0.6",
@@ -724,7 +724,7 @@
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="], "@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
"@gitbook/api": ["@gitbook/api@0.145.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-pq+lqUPdvrVstpojs7uOimaNePg16uE1X2Dx7VDulqResmNp/FiV8a1i99vlYHhfhVA/U7i6wAN0iX4zi0/YZA=="], "@gitbook/api": ["@gitbook/api@0.146.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-gWcSCbN+9Zc/XOEk4t8v70kKyaVJQytHMnnstArr8av1YpHzZWEpVQCeQ20SnJvkvO5y+P7TCVxJCLG2ciT9SQ=="],
"@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"], "@gitbook/browser-types": ["@gitbook/browser-types@workspace:packages/browser-types"],
+1 -1
View File
@@ -42,7 +42,7 @@
"catalog": { "catalog": {
"@tsconfig/strictest": "^2.0.6", "@tsconfig/strictest": "^2.0.6",
"@tsconfig/node20": "^20.1.6", "@tsconfig/node20": "^20.1.6",
"@gitbook/api": "0.145.0", "@gitbook/api": "0.146.0",
"@scalar/api-client-react": "^1.3.46", "@scalar/api-client-react": "^1.3.46",
"@types/react": "^19.0.0", "@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0", "@types/react-dom": "^19.0.0",
@@ -1,4 +1,4 @@
import type { DocumentInlineIcon, DocumentMarkColor } from '@gitbook/api'; import type { DocumentInlineIcon } from '@gitbook/api';
import { tcls } from '@/lib/tailwind'; import { tcls } from '@/lib/tailwind';
import { Icon, type IconName } from '@gitbook/icons'; import { Icon, type IconName } from '@gitbook/icons';
@@ -7,14 +7,11 @@ import { textColorToStyle } from './utils/colors';
export async function InlineIcon(props: InlineProps<DocumentInlineIcon>) { export async function InlineIcon(props: InlineProps<DocumentInlineIcon>) {
const { inline } = props; const { inline } = props;
const icon = inline.data.icon as IconName; const { color, icon } = inline.data;
const color = inline.data.color
? (inline.data.color as DocumentMarkColor['data']['text'])
: undefined;
return ( return (
<Icon <Icon
icon={icon} icon={icon as IconName}
className={tcls('inline size-[1em]', color ? textColorToStyle[color] : null)} className={tcls('inline size-[1em]', color ? textColorToStyle[color] : null)}
/> />
); );
@@ -85,11 +85,13 @@ export async function RecordCard(
light: { light: {
src: lightCover.href, src: lightCover.href,
size: lightCover.file?.dimensions, size: lightCover.file?.dimensions,
alt: light.alt,
}, },
dark: darkCover dark: darkCover
? { ? {
src: darkCover.href, src: darkCover.href,
size: darkCover.file?.dimensions, size: darkCover.file?.dimensions,
alt: dark.alt,
} }
: null, : null,
}} }}
@@ -21,6 +21,12 @@ export function getRecordValue<T extends number | string | boolean | string[] |
return record.values[definitionId]; return record.values[definitionId];
} }
type RecordCardCover = {
contentRef: ContentRefFile | ContentRefURL | null;
objectFit?: CardsImageObjectFit;
alt?: string;
};
/** /**
* Get the covers for a record card. * Get the covers for a record card.
* Returns both the light and dark covers with their content refs and optional object fit. * Returns both the light and dark covers with their content refs and optional object fit.
@@ -31,10 +37,7 @@ export function getRecordCardCovers(
record: DocumentTableRecord, record: DocumentTableRecord,
view: DocumentTableViewCards view: DocumentTableViewCards
): { ): {
[key in 'light' | 'dark']: { [key in 'light' | 'dark']: RecordCardCover;
contentRef: ContentRefFile | ContentRefURL | null;
objectFit?: CardsImageObjectFit;
};
} { } {
const lightValue = view.coverDefinition const lightValue = view.coverDefinition
? (getRecordValue(record, view.coverDefinition) as DocumentTableImageRecord | string[]) ? (getRecordValue(record, view.coverDefinition) as DocumentTableImageRecord | string[])
@@ -53,10 +56,9 @@ export function getRecordCardCovers(
/** /**
* Process a cover value and return the content ref and object fit. * Process a cover value and return the content ref and object fit.
*/ */
function processCoverValue(value: DocumentTableImageRecord | string[] | null | undefined): { function processCoverValue(
contentRef: ContentRefFile | ContentRefURL | null; value: DocumentTableImageRecord | string[] | null | undefined
objectFit?: CardsImageObjectFit; ): RecordCardCover {
} {
if (!value) { if (!value) {
return { contentRef: null }; return { contentRef: null };
} }
@@ -78,6 +80,7 @@ function processCoverValue(value: DocumentTableImageRecord | string[] | null | u
return { return {
contentRef: imageValue.ref, contentRef: imageValue.ref,
objectFit: imageValue.objectFit, objectFit: imageValue.objectFit,
alt: imageValue.alt,
}; };
} }
@@ -8,16 +8,21 @@ import type { PolymorphicComponentProp } from './types';
export type ImageSize = { width: number; height: number }; export type ImageSize = { width: number; height: number };
type ImageSource = { type DefaultImageSource = {
src: string; src: string;
size?: ImageSize;
aspectRatio?: string; aspectRatio?: string;
/**
* Override the alt attribute.
*/
alt?: string;
}; };
export type ImageSourceSized = { type ImageSource = DefaultImageSource & {
src: string; size?: ImageSize;
};
export type ImageSourceSized = DefaultImageSource & {
size: ImageSize | null; size: ImageSize | null;
aspectRatio?: string;
}; };
export type ImageResponsiveSize = { export type ImageResponsiveSize = {
@@ -105,7 +110,7 @@ export function Image(
} & ImageCommonProps } & ImageCommonProps
> >
) { ) {
const { sources, style, inline = false, ...rest } = props; const { sources, style, inline = false, alt, ...rest } = props;
return ( return (
<> <>
@@ -119,6 +124,7 @@ export function Image(
sources.dark ? 'dark:hidden' : null, sources.dark ? 'dark:hidden' : null,
style style
)} )}
alt={sources.light.alt || alt}
/> />
{sources.dark ? ( {sources.dark ? (
<ImagePicture <ImagePicture
@@ -134,6 +140,7 @@ export function Image(
inline ? 'dark:inline' : 'dark:block', inline ? 'dark:inline' : 'dark:block',
style style
)} )}
alt={sources.dark.alt || sources.light.alt || alt}
/> />
) : null} ) : null}
</> </>