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
7 changes: 4 additions & 3 deletions docs/server.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Bluefin Server
hide_title: true
description: An image-based Linux server OS built like a container, composed from freedesktop-sdk.
slug: /server
custom_edit_url: https://github.com/projectbluefin/server/edit/main/README.md
Expand All @@ -13,9 +14,9 @@ import {
} from "@site/src/components/ImageProductSection";
import { PerImageUsage } from "@site/src/components/analytics/CountmeAnalyticsCharts";

<ImageIdentity product="server" />

<ServerReadme />
<ImageIdentity product="server">
<ServerReadme />
</ImageIdentity>

## Downloads & Image Versions

Expand Down
8 changes: 3 additions & 5 deletions docs/skills/variant-docs-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,8 @@ motivation prose — see _Never write in a maintainer's voice_ in `AGENTS.md`.
no second route and needs no sidebar or global Markdown configuration changes.
Its `mdx.format: md` frontmatter preserves ordinary Markdown and raw HTML;
the wrapper retains native headings, TOC, search, and the upstream edit link.
Keep `title` in frontmatter for navigation and the single top-level `h1`.
Do not set `hide_title: true`; `scripts/fetch-readmes.mjs` strips any leading
`# Title` heading from the imported README when `product` is specified so
Docusaurus emits exactly one page title.

Keep `title` in frontmatter for navigation and metadata. When an image logo already contains the product wordmark, set `hide_title: true` so the visual page header avoids duplicate title text while `<ImageIdentity>` preserves an accessible screen-reader `<h1>`.
`scripts/fetch-readmes.mjs` strips leading `# Title` headings and scale comparison silhouettes from imported product READMEs so the landing page retains a clean single-character identity and flow.
`scripts/fetch-readmes.mjs` rewrites parsed links/images, not code examples.
It reads its source list from `src/components/image-identities.json`.
Because `format: md` passes raw HTML through to the page and the seeds are
Expand Down Expand Up @@ -166,6 +163,7 @@ card entry from `IMAGES` in `ImageCards.tsx`.
explicit, and link build-time recipes to upstream repositories. When headings
are simplified, place explicit HTML anchors (`<a id="..."></a>`) so legacy links
do not 404.

## Common Rationalizations

| Rationalization | Reality |
Expand Down
9 changes: 8 additions & 1 deletion scripts/fetch-readmes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,9 @@ export async function prepareReadme(
const resolve = (value, image) => {
if (/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(value)) return value;
if (product === "server" && !image) {
const match = value.match(/^(?:docs\/skills\/)?([^/#?]+?\.md)(?:#(.+))?$/);
const match = value.match(
/^(?:docs\/skills\/)?([^/#?]+?\.md)(?:#(.+))?$/,
);
if (match && serverIndexedDocs.has(match[1])) {
const slug = match[1].slice(0, -3);
return match[2] ? `/server/${slug}/#${match[2]}` : `/server/${slug}/`;
Expand All @@ -266,6 +268,11 @@ export async function prepareReadme(
identity.logoLight,
identity.logoDark,
...identity.images.map((image) => image.src),
...(product === "server"
? [
"https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg",
]
: []),
].filter(Boolean),
);
const matchesArtwork = (url) => artwork.has(resolve(url, true));
Expand Down
14 changes: 14 additions & 0 deletions scripts/fetch-readmes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ test("server indexed docs resolve to local documentation routes", async () => {
/\[Architecture Roadmap\]\(https:\/\/github.com\/projectbluefin\/server\/blob\/main\/docs\/skills\/architecture-roadmap.md\)/,
);
});
test("server product README strips scale comparison silhouette image", async () => {
const serverSource = {
product: "server",
repo: "projectbluefin/server",
ref: "main",
readmePath: "README.md",
};
const input =
"> An image-based Linux server OS built like a container\n\n" +
"![armargasaurus](https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg)\n\n" +
"## Release status\n";
const result = await prepareReadme(input, serverSource);
assert.doesNotMatch(result, /Dicraeosauridae_Scale\.svg/);
});

test("a failed refresh preserves the offline seed rather than publishing an empty README", async () => {
const directory = await mkdtemp(join(tmpdir(), "readme-import-"));
Expand Down
110 changes: 110 additions & 0 deletions src/components/ImageIdentity.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
margin-bottom: 2rem;
}

.srOnly {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

.wordmark {
display: inline-block;
max-width: 100%;
Expand Down Expand Up @@ -121,3 +133,101 @@
.sources {
margin-bottom: 0;
}

/* ── Hero top of page layout with wrapping text ───────────────────────── */

.identityHero {
margin-bottom: 2.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--ifm-color-emphasis-200);
}

.heroTop {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 1rem 1.5rem;
margin-bottom: 1.5rem;
}

.heroTop .wordmark {
flex-shrink: 0;
}

.heroTop .species {
margin: 0;
font-size: 0.95rem;
color: var(--ifm-color-emphasis-700);
}

/* Float character artwork right so sibling prose flows around it */
.heroContent::after {
content: "";
display: table;
clear: both;
}

.heroCharacter {
float: right;
width: 320px;
max-width: 42%;
margin: 0 0 1.5rem 2rem;
text-align: center;
}

.heroCharacter img {
display: block;
width: 100%;
height: auto;
max-height: 380px;
object-fit: contain;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.15));
}

[data-theme="dark"] .heroCharacter img {
filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.45));
}
.heroCharacter figcaption {
font-size: 0.8rem;
color: var(--ifm-color-emphasis-600);
margin-top: 0.4rem;
}

.heroProse {
font-size: 1.05rem;
line-height: 1.65;
color: var(--ifm-font-color-base);
}

.heroProse blockquote {
margin: 0 0 1rem;
padding: 0.75rem 1rem;
font-size: 1.15rem;
font-weight: 500;
color: var(--ifm-color-primary-darker);
border-left: 4px solid var(--ifm-color-primary);
background: var(--ifm-color-emphasis-100);
border-radius: 0 var(--ifm-global-radius) var(--ifm-global-radius) 0;
}

[data-theme="dark"] .heroProse blockquote {
color: var(--ifm-color-primary-lightest);
background: var(--ifm-color-emphasis-200);
}

.heroProse p {
margin-bottom: 1rem;
}

@media (max-width: 768px) {
.heroTop {
gap: 0.5rem 1rem;
}

.heroCharacter {
float: none;
width: 100%;
max-width: 260px;
margin: 0 auto 1.5rem;
}
}
76 changes: 75 additions & 1 deletion src/components/ImageIdentity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,84 @@ import styles from "./ImageIdentity.module.css";

export type ImageProduct = keyof typeof identities;

export default function ImageIdentity({ product }: { product: ImageProduct }) {
export default function ImageIdentity({
product,
children,
}: {
product: ImageProduct;
children?: React.ReactNode;
}) {
const identity = identities[product];
const readme = `https://github.com/${identity.repo}/blob/${identity.ref}/README.md`;

if (children) {
const primaryImage = identity.images[0];
return (
<header className={`${styles.identity} ${styles.identityHero}`}>
<h1 className={styles.srOnly}>{identity.name}</h1>
<div className={styles.heroTop}>
{identity.logoLight && (
<a
href="https://github.com/projectbluefin/artwork"
className={styles.wordmark}
>
<img
className={styles.logoLight}
src={identity.logoLight}
alt={identity.name}
width="400"
/>
<img
className={styles.logoDark}
src={identity.logoDark}
alt={identity.name}
width="400"
/>
</a>
)}
<p className={styles.species}>
<a href={identity.wikipedia}>
<i>{identity.species}</i>
</a>
</p>
</div>
<div className={styles.heroContent}>
{primaryImage && (
<figure className={styles.heroCharacter}>
<a href={primaryImage.source}>
<img
src={primaryImage.src}
alt={primaryImage.alt}
loading="eager"
decoding="async"
/>
</a>
<figcaption>
<a href={primaryImage.source}>{primaryImage.attribution}</a>
</figcaption>
</figure>
)}
<div className={styles.heroProse}>{children}</div>
</div>
<p className={styles.sources}>
<a href={readme}>Upstream README</a>
{" · "}
<a href="https://github.com/projectbluefin/artwork">Artwork source</a>
{" · "}
<a href="https://github.com/projectbluefin/artwork/blob/main/LICENSE">
Artwork license
</a>
{identity.testsuiteRun && (
<>
{" · "}
<a href={identity.testsuiteRun}>QA verification run</a>
</>
)}
</p>
</header>
);
}

return (
<header className={styles.identity}>
{identity.logoLight && (
Expand Down
7 changes: 0 additions & 7 deletions src/components/image-identities.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
"source": "/dinosaurs#kubernetes",
"attribution": "Jacob Schnurr · Project Bluefin artwork",
"diagram": false
},
{
"src": "https://upload.wikimedia.org/wikipedia/commons/d/d8/Dicraeosauridae_Scale.svg",
"alt": "Dicraeosauridae silhouette size comparison, including Amargasaurus and a human figure",
"source": "https://commons.wikimedia.org/wiki/File:Dicraeosauridae_Scale.svg",
"attribution": "Slate Weasel · Public domain",
"diagram": true
}
]
},
Expand Down
Loading
Loading