Skip to content

Commit 3af5334

Browse files
committed
fix(theme)!: component HoverkraftHero supportingVisual accepts a image path
Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent a768a06 commit 3af5334

File tree

10 files changed

+111
-59
lines changed

10 files changed

+111
-59
lines changed

packages/docs/content/components/hero.md

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ The Hoverkraft theme ships a `HoverkraftHero` component that encapsulates the br
1414

1515
## Props
1616

17-
| Prop | Type | Required | Default | Description |
18-
| ------------------ | -------------------------- | -------- | ------------ | --------------------------------------------------- |
19-
| `title` | `ReactNode` || - | Main heading text |
20-
| `description` | `ReactNode` || - | Subheading text |
21-
| `eyebrow` | `string` || - | Small text above title |
22-
| `brandedText` | `ReactNode` || - | Deprecated: use `HoverkraftBrandHighlight` in title |
23-
| `supportingVisual` | `ReactNode` || - | Image or graphic to display |
24-
| `actions` | `HoverkraftAction[]` || - | Call-to-action buttons |
25-
| `align` | `'left' \| 'center'` || `'left'` | Text alignment and grid alignment |
26-
| `tone` | `'midnight' \| 'daylight'` || `'midnight'` | Color scheme (dark or light background) |
27-
| `id` | `string` || - | Optional DOM ID for deep-linking |
28-
| `className` | `string` || - | Extend styling with additional classes |
17+
| Prop | Type | Required | Default | Description |
18+
| --------------------- | -------------------------- | -------- | ------------ | --------------------------------------------------- |
19+
| `title` | `ReactNode` || - | Main heading text |
20+
| `description` | `ReactNode` || - | Subheading text |
21+
| `eyebrow` | `string` || - | Small text above title |
22+
| `brandedText` | `ReactNode` || - | Deprecated: use `HoverkraftBrandHighlight` in title |
23+
| `supportingVisual` | `string` || - | Path to the supporting image (e.g., `img/hero.png`) |
24+
| `supportingVisualAlt` | `string` || `""` | Alt text describing the supporting image |
25+
| `actions` | `HoverkraftAction[]` || - | Call-to-action buttons |
26+
| `align` | `'left' \| 'center'` || `'left'` | Text alignment and grid alignment |
27+
| `tone` | `'midnight' \| 'daylight'` || `'midnight'` | Color scheme (dark or light background) |
28+
| `id` | `string` || - | Optional DOM ID for deep-linking |
29+
| `className` | `string` || - | Extend styling with additional classes |
2930

3031
### Action Object Structure
3132

@@ -75,25 +76,7 @@ const actions = [
7576
},
7677
];
7778

78-
const supportingVisual = (
79-
<div
80-
style={{
81-
display: "grid",
82-
placeItems: "center",
83-
width: "100%",
84-
aspectRatio: "4 / 3",
85-
borderRadius: "1rem",
86-
background:
87-
"radial-gradient(circle at 30% 30%, rgba(255,255,255,0.25), transparent 55%), linear-gradient(135deg, #1b2735, #090a0f)",
88-
color: "#fff",
89-
fontWeight: 600,
90-
fontSize: "1.15rem",
91-
}}
92-
aria-hidden="true"
93-
>
94-
Midnight tone
95-
</div>
96-
);
79+
const supportingVisual = "img/home.png";
9780

9881
render(
9982
<HoverkraftHero
@@ -108,6 +91,7 @@ render(
10891
tone="midnight"
10992
align="center"
11093
supportingVisual={supportingVisual}
94+
supportingVisualAlt="Screenshot of Hoverkraft shown in midnight tone"
11195
id="hero-midnight"
11296
/>
11397
);
@@ -121,25 +105,7 @@ const actions = [
121105
{ label: "Configuration", to: "/docs/configuration", variant: "secondary" },
122106
];
123107

124-
const supportingVisual = (
125-
<div
126-
style={{
127-
display: "grid",
128-
placeItems: "center",
129-
width: "100%",
130-
aspectRatio: "4 / 3",
131-
borderRadius: "1rem",
132-
background:
133-
"radial-gradient(circle at 60% 30%, rgba(20, 60, 120, 0.15), transparent 55%), linear-gradient(135deg, #f6f8fb, #ffffff)",
134-
color: "#1c2738",
135-
fontWeight: 600,
136-
fontSize: "1.15rem",
137-
}}
138-
aria-hidden="true"
139-
>
140-
Daylight tone
141-
</div>
142-
);
108+
const supportingVisual = "img/home.png";
143109

144110
render(
145111
<HoverkraftHero
@@ -155,6 +121,7 @@ render(
155121
tone="daylight"
156122
align="left"
157123
supportingVisual={supportingVisual}
124+
supportingVisualAlt="Screenshot of Hoverkraft shown in daylight tone"
158125
id="hero-daylight"
159126
/>
160127
);

packages/docs/content/examples.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export default function Home() {
3333
</>
3434
}
3535
description="Your gateway to open-source innovation."
36-
supportingVisual={<img src="/img/home.png" alt="Platform" />}
36+
supportingVisual="/static/img/home.png"
37+
supportingVisualAlt="Screenshot of the Hoverkraft platform"
3738
actions={[
3839
{ label: "Explore Projects", to: "/docs/intro", variant: "primary" },
3940
{
@@ -77,7 +78,6 @@ export default function Home() {
7778
/>
7879
</div>
7980
</section>
80-
8181
{/* Projects Grid */}
8282
<section style={{ padding: "4rem 0" }}>
8383
<div style={{ maxWidth: "1200px", margin: "0 auto", padding: "0 1rem" }}>
@@ -316,7 +316,8 @@ You can use multiple hero sections with different tones:
316316
description="Highlight another key feature"
317317
tone="daylight"
318318
align="left"
319-
supportingVisual={<img src="/img/feature.png" alt="Feature" />}
319+
supportingVisual="img/home.png"
320+
supportingVisualAlt="Illustration of the highlighted feature"
320321
/>
321322
</>
322323
```

packages/docs/src/pages/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ export default function Home(): JSX.Element {
9595
<HoverkraftHero
9696
title={
9797
<>
98-
<HoverkraftBrandHighlight>Hoverkraft</HoverkraftBrandHighlight> Theme for{" "}
99-
Docusaurus
98+
<HoverkraftBrandHighlight>Hoverkraft</HoverkraftBrandHighlight> Theme for Docusaurus
10099
</>
101100
}
102101
description="Craft documentation that feels premium out-of-the-box. Opinionated visual language meets pragmatic components that let your content shine."
103102
actions={heroActions}
103+
supportingVisual="/img/home.png"
104104
tone="midnight"
105105
align="center"
106106
/>

packages/docs/static/.nojekyll

Whitespace-only changes.

packages/docs/static/img/home.png

1.5 MB
Loading

packages/theme/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
"testEnvironment": "jsdom",
7878
"moduleNameMapper": {
7979
"\\.(css|less|scss|sass)$": "identity-obj-proxy",
80-
"^@docusaurus/Link$": "<rootDir>/src/test-utils/docusaurusLinkMock.tsx"
80+
"^@docusaurus/Link$": "<rootDir>/src/test-utils/docusaurusLinkMock.tsx",
81+
"^@docusaurus/useBaseUrl$": "<rootDir>/src/test-utils/docusaurusUseBaseUrlMock.ts"
8182
},
8283
"testMatch": [
8384
"**/__tests__/**/*.[jt]s?(x)",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type UseBaseUrlOptions = {
2+
forcePrependBaseUrl?: boolean;
3+
absolute?: boolean;
4+
};
5+
6+
export default function useBaseUrl(targetUrl?: string, _options?: UseBaseUrlOptions): string {
7+
void _options;
8+
if (typeof targetUrl !== "string") {
9+
return "";
10+
}
11+
12+
return targetUrl;
13+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from "react";
2+
import { renderToStaticMarkup } from "react-dom/server";
3+
4+
import { HoverkraftHero } from "../hoverscape/HoverkraftHero";
5+
6+
describe("HoverkraftHero", () => {
7+
it("renders supporting image when a path is provided", () => {
8+
const markup = renderToStaticMarkup(
9+
<HoverkraftHero
10+
title="Hoverkraft"
11+
supportingVisual="/img/example.png"
12+
supportingVisualAlt="Dashboard screenshot"
13+
/>
14+
);
15+
16+
expect(markup).toContain("img");
17+
expect(markup).toContain('src="/img/example.png"');
18+
expect(markup).toContain('alt="Dashboard screenshot"');
19+
});
20+
21+
it("omits supporting image when the path is empty", () => {
22+
const markup = renderToStaticMarkup(
23+
<HoverkraftHero title="Hoverkraft" supportingVisual=" " />
24+
);
25+
26+
expect(markup).not.toContain("<img");
27+
});
28+
});

packages/theme/src/theme/hoverscape/HoverkraftHero.module.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,32 @@
109109
align-items: center;
110110
}
111111

112+
.floatingCard {
113+
background: rgba(255, 255, 255, 0.95);
114+
border-radius: 16px;
115+
padding: 0;
116+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
117+
max-width: 400px;
118+
animation: float 6s ease-in-out infinite;
119+
backdrop-filter: blur(10px);
120+
}
121+
122+
@keyframes float {
123+
0%,
124+
100% {
125+
transform: translateY(0px);
126+
}
127+
50% {
128+
transform: translateY(-10px);
129+
}
130+
}
131+
132+
.heroVisualImage {
133+
display: block;
134+
max-width: 100%;
135+
height: auto;
136+
}
137+
112138
@media (max-width: 880px) {
113139
.heroContainer {
114140
grid-template-columns: 1fr;

packages/theme/src/theme/hoverscape/HoverkraftHero.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactNode } from "react";
22
import clsx from "clsx";
3+
import useBaseUrl from "@docusaurus/useBaseUrl";
34
import styles from "./HoverkraftHero.module.css";
45
import { HoverkraftButton } from "./HoverkraftButton";
56
import { HoverkraftBrandHighlight } from "./HoverkraftBrandHighlight";
@@ -10,7 +11,8 @@ export interface HoverkraftHeroProps {
1011
title: ReactNode;
1112
description?: ReactNode;
1213
brandedText?: ReactNode;
13-
supportingVisual?: ReactNode;
14+
supportingVisual?: string;
15+
supportingVisualAlt?: string;
1416
actions?: HoverkraftAction[];
1517
align?: "left" | "center";
1618
tone?: "midnight" | "daylight";
@@ -38,13 +40,15 @@ export function HoverkraftHero({
3840
description,
3941
brandedText,
4042
supportingVisual,
43+
supportingVisualAlt = "",
4144
actions,
4245
align = "left",
4346
tone = "midnight",
4447
id,
4548
className,
4649
}: HoverkraftHeroProps) {
47-
const hasSupportingVisual = supportingVisual !== null && supportingVisual !== undefined;
50+
const hasSupportingVisual =
51+
typeof supportingVisual === "string" && supportingVisual.trim().length > 0;
4852
const highlightContent =
4953
brandedText !== null && brandedText !== undefined ? (
5054
<HoverkraftBrandHighlight>{brandedText}</HoverkraftBrandHighlight>
@@ -73,7 +77,19 @@ export function HoverkraftHero({
7377
</div>
7478
)}
7579
</div>
76-
{hasSupportingVisual && <div className={styles.heroVisual}>{supportingVisual}</div>}
80+
{hasSupportingVisual && (
81+
<div className={styles.heroVisual}>
82+
<div className={styles.floatingCard}>
83+
<img
84+
src={useBaseUrl(supportingVisual)}
85+
alt={supportingVisualAlt ?? ""}
86+
className={styles.heroVisualImage}
87+
loading="lazy"
88+
decoding="async"
89+
/>
90+
</div>
91+
</div>
92+
)}
7793
</div>
7894
</section>
7995
);

0 commit comments

Comments
 (0)