Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 17 additions & 50 deletions packages/docs/content/components/hero.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@ The Hoverkraft theme ships a `HoverkraftHero` component that encapsulates the br

## Props

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

### Action Object Structure

Expand Down Expand Up @@ -75,25 +76,7 @@ const actions = [
},
];

const supportingVisual = (
<div
style={{
display: "grid",
placeItems: "center",
width: "100%",
aspectRatio: "4 / 3",
borderRadius: "1rem",
background:
"radial-gradient(circle at 30% 30%, rgba(255,255,255,0.25), transparent 55%), linear-gradient(135deg, #1b2735, #090a0f)",
color: "#fff",
fontWeight: 600,
fontSize: "1.15rem",
}}
aria-hidden="true"
>
Midnight tone
</div>
);
const supportingVisual = "img/home.png";

render(
<HoverkraftHero
Expand All @@ -108,6 +91,7 @@ render(
tone="midnight"
align="center"
supportingVisual={supportingVisual}
supportingVisualAlt="Screenshot of Hoverkraft shown in midnight tone"
id="hero-midnight"
/>
);
Expand All @@ -121,25 +105,7 @@ const actions = [
{ label: "Configuration", to: "/docs/configuration", variant: "secondary" },
];

const supportingVisual = (
<div
style={{
display: "grid",
placeItems: "center",
width: "100%",
aspectRatio: "4 / 3",
borderRadius: "1rem",
background:
"radial-gradient(circle at 60% 30%, rgba(20, 60, 120, 0.15), transparent 55%), linear-gradient(135deg, #f6f8fb, #ffffff)",
color: "#1c2738",
fontWeight: 600,
fontSize: "1.15rem",
}}
aria-hidden="true"
>
Daylight tone
</div>
);
const supportingVisual = "img/home.png";

render(
<HoverkraftHero
Expand All @@ -155,6 +121,7 @@ render(
tone="daylight"
align="left"
supportingVisual={supportingVisual}
supportingVisualAlt="Screenshot of Hoverkraft shown in daylight tone"
id="hero-daylight"
/>
);
Expand Down
7 changes: 4 additions & 3 deletions packages/docs/content/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export default function Home() {
</>
}
description="Your gateway to open-source innovation."
supportingVisual={<img src="/img/home.png" alt="Platform" />}
supportingVisual="/static/img/home.png"
supportingVisualAlt="Screenshot of the Hoverkraft platform"
actions={[
{ label: "Explore Projects", to: "/docs/intro", variant: "primary" },
{
Expand Down Expand Up @@ -77,7 +78,6 @@ export default function Home() {
/>
</div>
</section>

{/* Projects Grid */}
<section style={{ padding: "4rem 0" }}>
<div style={{ maxWidth: "1200px", margin: "0 auto", padding: "0 1rem" }}>
Expand Down Expand Up @@ -316,7 +316,8 @@ You can use multiple hero sections with different tones:
description="Highlight another key feature"
tone="daylight"
align="left"
supportingVisual={<img src="/img/feature.png" alt="Feature" />}
supportingVisual="img/home.png"
supportingVisualAlt="Illustration of the highlighted feature"
/>
</>
```
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,12 @@ export default function Home(): JSX.Element {
<HoverkraftHero
title={
<>
<HoverkraftBrandHighlight>Hoverkraft</HoverkraftBrandHighlight> Theme for{" "}
Docusaurus
<HoverkraftBrandHighlight>Hoverkraft</HoverkraftBrandHighlight> Theme for Docusaurus
</>
}
description="Craft documentation that feels premium out-of-the-box. Opinionated visual language meets pragmatic components that let your content shine."
actions={heroActions}
supportingVisual="/img/home.png"
tone="midnight"
align="center"
/>
Expand Down
Empty file added packages/docs/static/.nojekyll
Empty file.
Binary file added packages/docs/static/img/home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion packages/theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy",
"^@docusaurus/Link$": "<rootDir>/src/test-utils/docusaurusLinkMock.tsx"
"^@docusaurus/Link$": "<rootDir>/src/test-utils/docusaurusLinkMock.tsx",
"^@docusaurus/useBaseUrl$": "<rootDir>/src/test-utils/docusaurusUseBaseUrlMock.ts"
},
"testMatch": [
"**/__tests__/**/*.[jt]s?(x)",
Expand Down
13 changes: 13 additions & 0 deletions packages/theme/src/test-utils/docusaurusUseBaseUrlMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type UseBaseUrlOptions = {
forcePrependBaseUrl?: boolean;
absolute?: boolean;
};

export default function useBaseUrl(targetUrl?: string, _options?: UseBaseUrlOptions): string {
void _options;
if (typeof targetUrl !== "string") {
return "";
}

return targetUrl;
}
28 changes: 28 additions & 0 deletions packages/theme/src/theme/__tests__/HoverkraftHero.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";

import { HoverkraftHero } from "../hoverscape/HoverkraftHero";

describe("HoverkraftHero", () => {
it("renders supporting image when a path is provided", () => {
const markup = renderToStaticMarkup(
<HoverkraftHero
title="Hoverkraft"
supportingVisual="/img/example.png"
supportingVisualAlt="Dashboard screenshot"
/>
);

expect(markup).toContain("img");
expect(markup).toContain('src="/img/example.png"');
expect(markup).toContain('alt="Dashboard screenshot"');
});

it("omits supporting image when the path is empty", () => {
const markup = renderToStaticMarkup(
<HoverkraftHero title="Hoverkraft" supportingVisual=" " />
);

expect(markup).not.toContain("<img");
});
});
26 changes: 26 additions & 0 deletions packages/theme/src/theme/hoverscape/HoverkraftHero.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,32 @@
align-items: center;
}

.floatingCard {
background: rgba(255, 255, 255, 0.95);
border-radius: 16px;
padding: 0;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
max-width: 400px;
animation: float 6s ease-in-out infinite;
backdrop-filter: blur(10px);
}

@keyframes float {
0%,
100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}

.heroVisualImage {
display: block;
max-width: 100%;
height: auto;
}

@media (max-width: 880px) {
.heroContainer {
grid-template-columns: 1fr;
Expand Down
22 changes: 19 additions & 3 deletions packages/theme/src/theme/hoverscape/HoverkraftHero.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ReactNode } from "react";
import clsx from "clsx";
import useBaseUrl from "@docusaurus/useBaseUrl";
import styles from "./HoverkraftHero.module.css";
import { HoverkraftButton } from "./HoverkraftButton";
import { HoverkraftBrandHighlight } from "./HoverkraftBrandHighlight";
Expand All @@ -10,7 +11,8 @@ export interface HoverkraftHeroProps {
title: ReactNode;
description?: ReactNode;
brandedText?: ReactNode;
supportingVisual?: ReactNode;
supportingVisual?: string;
supportingVisualAlt?: string;
actions?: HoverkraftAction[];
align?: "left" | "center";
tone?: "midnight" | "daylight";
Expand Down Expand Up @@ -38,13 +40,15 @@ export function HoverkraftHero({
description,
brandedText,
supportingVisual,
supportingVisualAlt = "",
actions,
align = "left",
tone = "midnight",
id,
className,
}: HoverkraftHeroProps) {
const hasSupportingVisual = supportingVisual !== null && supportingVisual !== undefined;
const hasSupportingVisual =
typeof supportingVisual === "string" && supportingVisual.trim().length > 0;
const highlightContent =
brandedText !== null && brandedText !== undefined ? (
<HoverkraftBrandHighlight>{brandedText}</HoverkraftBrandHighlight>
Expand Down Expand Up @@ -73,7 +77,19 @@ export function HoverkraftHero({
</div>
)}
</div>
{hasSupportingVisual && <div className={styles.heroVisual}>{supportingVisual}</div>}
{hasSupportingVisual && (
<div className={styles.heroVisual}>
<div className={styles.floatingCard}>
<img
src={useBaseUrl(supportingVisual)}
alt={supportingVisualAlt ?? ""}
className={styles.heroVisualImage}
loading="lazy"
decoding="async"
/>
</div>
</div>
)}
</div>
</section>
);
Expand Down
Loading