Skip to content

Commit 91b43d4

Browse files
authored
Merge pull request #2171 from mateuszbartosik/RDoc-3565
RDoc-3565 Implement ThemedImages for Card components
2 parents 652f610 + eb4f1f0 commit 91b43d4

File tree

6 files changed

+269
-10
lines changed

6 files changed

+269
-10
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
import ThemedImage from "@theme/ThemedImage";
3+
4+
export interface CardImageProps {
5+
imgSrc: string | { light: string; dark: string };
6+
imgAlt?: string;
7+
className?: string;
8+
}
9+
10+
export default function CardImage({
11+
imgSrc,
12+
imgAlt = "",
13+
className = "",
14+
}: CardImageProps) {
15+
const isThemedImage = typeof imgSrc === "object" && imgSrc !== null;
16+
17+
if (isThemedImage) {
18+
return (
19+
<ThemedImage
20+
alt={imgAlt}
21+
sources={{
22+
light: imgSrc.light,
23+
dark: imgSrc.dark,
24+
}}
25+
className={className}
26+
/>
27+
);
28+
}
29+
30+
return (
31+
<img
32+
src={imgSrc as string}
33+
alt={imgAlt}
34+
className={className}
35+
/>
36+
);
37+
}
38+

src/components/Common/CardWithImage.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ import Button, { type ButtonVariant } from "@site/src/components/Common/Button";
44
import { Icon } from "@site/src/components/Common/Icon";
55
import { IconName } from "@site/src/typescript/iconName";
66
import Badge from "@site/src/components/Common/Badge";
7+
import CardImage from "@site/src/components/Common/CardImage";
78
import isInternalUrl from "@docusaurus/isInternalUrl";
89

910
export interface CardWithImageProps {
1011
title: string;
1112
description: ReactNode;
12-
imgSrc: string;
13+
imgSrc: string | { light: string; dark: string };
1314
imgAlt?: string;
1415
url?: string;
1516
buttonVariant?: ButtonVariant;
1617
ctaLabel?: string;
17-
iconName?: IconName;
1818
imgIcon?: IconName;
1919
}
2020

@@ -26,17 +26,16 @@ export default function CardWithImage({
2626
url,
2727
buttonVariant = "secondary",
2828
ctaLabel = "Read now",
29-
iconName,
3029
imgIcon,
3130
}: CardWithImageProps) {
3231
const hasImage = Boolean(imgSrc);
3332
return (
3433
<div className="card group flex flex-col overflow-hidden rounded-2xl border border-black/10 dark:border-white/10 bg-muted/40 p-4 transition-colors">
3534
<div className={`flex items-center justify-center rounded-xl mb-4 overflow-hidden relative aspect-[79/24] ${hasImage ? "bg-black/40" : "bg-gradient-to-b from-[#204879] to-[#0F1425] to-[70%]"}`}>
3635
{hasImage ? (
37-
<img
38-
src={imgSrc}
39-
alt={imgAlt}
36+
<CardImage
37+
imgSrc={imgSrc}
38+
imgAlt={imgAlt}
4039
className="pointer-events-none w-full h-full object-cover object-center transition-transform origin-bottom duration-500 group-hover:scale-105 group-hover:translate-y-1"
4140
/>
4241
) : (

src/components/Common/CardWithImageHorizontal.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import Heading from "@theme/Heading";
33
import Button, { type ButtonVariant } from "@site/src/components/Common/Button";
44
import { IconName } from "@site/src/typescript/iconName";
55
import Badge from "@site/src/components/Common/Badge";
6+
import CardImage from "@site/src/components/Common/CardImage";
67
import isInternalUrl from "@docusaurus/isInternalUrl";
78

89
export interface CardWithImageHorizontalProps {
910
title: string;
1011
description: ReactNode;
11-
imgSrc: string;
12+
imgSrc: string | { light: string; dark: string };
1213
imgAlt?: string;
1314
url?: string;
1415
buttonVariant?: ButtonVariant;
@@ -28,9 +29,9 @@ export default function CardWithImageHorizontal({
2829
return (
2930
<div className="card group !grid grid-cols-1 xl:grid-cols-[minmax(10rem,_230px)_1fr] 2xl:grid-cols-[minmax(10rem,_270px)_1fr] items-center gap-4 overflow-hidden rounded-2xl border border-black/10 dark:border-white/10 bg-muted/40 p-4 transition-colors">
3031
<div className="aspect-[537/281] overflow-hidden rounded-xl relative flex items-center">
31-
<img
32-
src={imgSrc}
33-
alt={imgAlt}
32+
<CardImage
33+
imgSrc={imgSrc}
34+
imgAlt={imgAlt}
3435
className="pointer-events-none w-full h-auto object-contain transition-transform origin-bottom duration-500 group-hover:scale-105"
3536
/>
3637
{url && !isInternalUrl(url) && (
3.05 KB
Loading
3.14 KB
Loading

templates/themed-images.mdx

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
title: "Themed Images"
3+
hide_table_of_contents: false
4+
sidebar_label: Themed Images
5+
---
6+
7+
import Admonition from '@theme/Admonition';
8+
import ThemedImage from '@theme/ThemedImage';
9+
import CardWithImage from "@site/src/components/Common/CardWithImage";
10+
import CardWithImageHorizontal from "@site/src/components/Common/CardWithImageHorizontal";
11+
import ColGrid from "@site/src/components/ColGrid";
12+
import lightImage from '@site/static/img/templates/light-image.png';
13+
import darkImage from '@site/static/img/templates/dark-image.png';
14+
15+
# Themed Images overview
16+
17+
This guide shows how to use `ThemedImage` component to display different images for light and dark themes, ensuring optimal visual appearance in both modes.
18+
19+
## ThemedImage
20+
21+
### Import
22+
23+
```mdx
24+
import ThemedImage from '@theme/ThemedImage';
25+
```
26+
27+
### Basic usage
28+
29+
````mdx
30+
import ThemedImage from '@theme/ThemedImage';
31+
import lightImage from '@site/static/img/templates/light-image.png';
32+
import darkImage from '@site/static/img/templates/dark-image.png';
33+
34+
<ThemedImage
35+
alt="Description of the image"
36+
sources={{
37+
light: lightImage,
38+
dark: darkImage,
39+
}}
40+
/>
41+
````
42+
43+
#### Live example
44+
45+
<ThemedImage
46+
alt="Example themed image"
47+
sources={{
48+
light: lightImage,
49+
dark: darkImage,
50+
}}
51+
/>
52+
53+
### Custom styling
54+
55+
You can add custom CSS classes to style the image:
56+
57+
```mdx
58+
<ThemedImage
59+
alt="Styled image"
60+
sources={{
61+
light: lightImage,
62+
dark: darkImage,
63+
}}
64+
className="border-4 border-dotted border-pink-500"
65+
/>
66+
```
67+
68+
#### Live example
69+
70+
<ThemedImage
71+
alt="Styled themed image"
72+
sources={{
73+
light: lightImage,
74+
dark: darkImage,
75+
}}
76+
className="border-4 border-dotted border-pink-500"
77+
/>
78+
79+
### Using with imported images
80+
81+
When importing images from your assets folder:
82+
83+
```mdx
84+
import ThemedImage from '@theme/ThemedImage';
85+
import lightImage from '@site/static/img/templates/light-image.png';
86+
import darkImage from '@site/static/img/templates/dark-image.png';
87+
88+
<ThemedImage
89+
alt="API vs Studio comparison"
90+
sources={{
91+
light: lightImage,
92+
dark: darkImage,
93+
}}
94+
/>
95+
```
96+
97+
## Using ThemedImage in Card components
98+
99+
Both `CardWithImage` and `CardWithImageHorizontal` support themed images by passing an object with `light` and `dark` properties to the `imgSrc` prop.
100+
101+
### CardWithImage with themed images
102+
103+
```mdx
104+
import CardWithImage from "@site/src/components/Common/CardWithImage";
105+
import lightImage from '@site/static/img/templates/light-image.png';
106+
import darkImage from '@site/static/img/templates/dark-image.png';
107+
108+
<CardWithImage
109+
title="Example Card"
110+
description="Card with themed image"
111+
imgSrc={{
112+
light: lightImage,
113+
dark: darkImage,
114+
}}
115+
imgAlt="Example card image"
116+
/>
117+
```
118+
119+
#### Live example
120+
121+
<ColGrid colCount={1}>
122+
<CardWithImage
123+
title="Example Card"
124+
description="Card with themed image support"
125+
imgSrc={{
126+
light: lightImage,
127+
dark: darkImage,
128+
}}
129+
imgAlt="Example card"
130+
/>
131+
</ColGrid>
132+
133+
### CardWithImageHorizontal with themed images
134+
135+
```mdx
136+
import CardWithImageHorizontal from "@site/src/components/Common/CardWithImageHorizontal";
137+
import lightImage from '@site/static/img/templates/light-image.png';
138+
import darkImage from '@site/static/img/templates/dark-image.png';
139+
140+
<CardWithImageHorizontal
141+
title="Horizontal Card"
142+
description="Horizontal card with themed image"
143+
imgSrc={{
144+
light: lightImage,
145+
dark: darkImage,
146+
}}
147+
imgAlt="Horizontal card image"
148+
/>
149+
```
150+
151+
#### Live example
152+
153+
<ColGrid colCount={1}>
154+
<CardWithImageHorizontal
155+
title="Horizontal Card"
156+
description="Horizontal card with themed image support"
157+
imgSrc={{
158+
light: lightImage,
159+
dark: darkImage,
160+
}}
161+
imgAlt="Horizontal card"
162+
/>
163+
</ColGrid>
164+
165+
### Fallback to single image
166+
167+
Both card components maintain backward compatibility. If you pass a string instead of an object, they'll use a regular `<img>` tag:
168+
169+
```mdx
170+
<CardWithImage
171+
title="Simple Card"
172+
description="Card with single image"
173+
imgSrc={singleImage}
174+
imgAlt="Card image"
175+
/>
176+
```
177+
#### Live example
178+
179+
<ColGrid colCount={1}>
180+
<CardWithImage
181+
title="Simple Card"
182+
description="Card with single image"
183+
imgSrc={lightImage}
184+
imgAlt="Card image"
185+
/>
186+
</ColGrid>
187+
188+
## Best practices
189+
190+
- **Always provide both images**: Ensure you have optimized versions for both light and dark themes.
191+
- **Use descriptive alt text**: Always include meaningful `alt` text for accessibility.
192+
- **Match image dimensions**: Keep light and dark variants at the same dimensions for consistent layout.
193+
- **Optimize images**: Use appropriate image formats and compression for web performance.
194+
- **Test both themes**: Verify that images look good in both light and dark modes.
195+
196+
<Admonition type="info" title="">
197+
When using themed images in card components, the component automatically detects the object format and renders the appropriate image component. For single images, use a string; for themed images, use an object with `light` and `dark` properties.
198+
</Admonition>
199+
200+
## Props
201+
202+
### ThemedImage
203+
204+
| Prop | Type | Default | Description |
205+
| --------- | --------------------------------------- | ------- | --------------------------------------------------------------------------- |
206+
| alt | string || Alternative text for the image (required for accessibility). |
207+
| sources | `{ light: string; dark: string }` || Object containing image sources for light and dark themes. |
208+
| className | string || Additional CSS classes for custom styling. |
209+
| width | number \| string || Image width (optional). |
210+
| height | number \| string || Image height (optional). |
211+
| style | React.CSSProperties || Inline styles for the image (optional). |
212+
213+
### CardWithImage / CardWithImageHorizontal
214+
215+
The `imgSrc` prop accepts either:
216+
217+
- **String**: Single image URL (backward compatible)
218+
- **Object**: `{ light: string; dark: string }` for themed images
219+
220+
When an object is provided, the component automatically uses `ThemedImage` internally.
221+

0 commit comments

Comments
 (0)