Skip to content

feat(driver-versions): add foil effects, Mesa/GNOME cards, CodeBlock refactor#694

Merged
castrojo merged 9 commits intoprojectbluefin:mainfrom
castrojo:upstream-pr/ci-cache-efficiency
Mar 31, 2026
Merged

feat(driver-versions): add foil effects, Mesa/GNOME cards, CodeBlock refactor#694
castrojo merged 9 commits intoprojectbluefin:mainfrom
castrojo:upstream-pr/ci-cache-efficiency

Conversation

@castrojo
Copy link
Copy Markdown
Contributor

Summary

  • Add gold/silver/green foil card effects for major/minor/NVIDIA driver version bumps
  • Add Mesa and GNOME version cards to the driver versions page
  • Replace custom CopyableCode widgets with Docusaurus built-in CodeBlock for consistency
  • Fix copy/paste widget global consistency across the images page
  • Fix minor bump display on latest card; hide null HWE Kernel field
  • Add sbom-attestations.json seed file to unblock cold-cache CI builds

Testing

CI passed on fork PR castrojo/documentation 27.

Assisted-by: Claude Sonnet 4.6 via GitHub Copilot
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

castrojo and others added 8 commits March 31, 2026 19:08
- custom.css: add global padding-right (3.5rem) on .theme-code-block pre so
  code text never slides under the absolutely-positioned copy button; add
  :focus-visible reveal for keyboard accessibility
- DriverVersionsCatalog.module.css: remove always-visible opacity:1 override
  (was inconsistent with every other widget); change commandBlock from
  inline-block/width:auto to block/width:100%
- FeedItems.module.css: add .copyButton:focus-visible rule so keyboard users
  can discover and use the copy button
- ImagesCatalog: add CopyableCode component — hover-reveal copy button with
  same visual language as FeedItems (border, icon ⎘/✓, 1.5s feedback); replace
  all bare <code> command blocks in StreamList and security tabs with it;
  add copyableCode + copyableCodeBtn CSS with proper padding-right so text
  never overlaps the button

Assisted-by: Claude Sonnet 4.6 via GitHub Copilot
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The primary bootc switch commands rendered inside the Tabs component
were using bare <code> elements with no copy affordance. Only the
secondary StreamList (testing streams) had CopyableCode. Replace both
entry.command and entry.nvidiaCommand bare <code> tags in the tab
render path with CopyableCode so all bootc switch lines have the
hover-reveal copy button.

Assisted-by: Claude Sonnet 4.6 via OpenCode
Swap the hand-rolled CopyableCode component (⎘/✓ hover-reveal button)
for the standard Docusaurus <CodeBlock language="bash"> widget used
by DriverVersionsCatalog, giving all bootc switch commands and security
commands the same syntax-highlighted, built-in copy button appearance
site-wide. Remove the now-dead .copyableCode/.copyableCodeBtn CSS rules.

Assisted-by: Claude Sonnet 4.6 via OpenCode
Apply the same gold and silver foil treatment from GitHubProfileCard
to the driver version bump indicators:

- Major bump cards: gold border (#e6c200), gold gradient background,
  gold holographic ::after overlay (mix-blend-mode: color-dodge),
  gold pill tag with warm-dark text
- Minor bump cards: silver border (#b8c6d8), silver gradient background,
  silver holographic ::after overlay, silver pill tag with cool-dark text
- Add z-index: 2 to .majorVersionLabel, .majorVersionValue so text
  renders above the foil overlay layer

Assisted-by: Claude Sonnet 4.6 via OpenCode
- nvidiaMajorBump: green foil (border #5a9900, gradient bg, green
  holographic ::after shimmer) matching the gold/silver pattern
- nvidiaMinorBump: lighter green foil treatment
- Add .nvidiaBumpTag (solid #76b900 bg, dark green text #1a3600) and
  .nvidiaMinorTag (light green #c8e090, dark text) — use these on NVIDIA
  cards so pill color matches card color rather than gold
- Fix .bumpTag and .minorTag: swap semi-transparent gradient backgrounds
  for solid opaque colors (#ffd700 / #c8d8e8) so text is legible in
  both light and dark mode

Assisted-by: Claude Sonnet 4.6 via OpenCode
- Add gnome field to VersionSet interface and buildRowFromApiRelease
- Add Mesa and GNOME version cards to driver versions timeline
- Reorder cards: Kernel → HWE Kernel → NVIDIA → Mesa → GNOME
- Fix HWE Kernel label (was 'HWE', now 'HWE Kernel')
- Add Mesa foil (warm orange) and GNOME foil (GNOME blue) with
  major/minor bump detection, matching existing kernel/NVIDIA patterns
- Add mesaCard, gnomeCard base styles with brand-tinted backgrounds
- Add mesaBumpTag, mesaMinorTag, gnomeBumpTag, gnomeMinorTag pill styles

Assisted-by: Claude Sonnet 4.6 via OpenCode
- Remove !emphasize guard from all minor bump tags and foil classes
  so minor bumps render on the latest/hero card (e.g. NVIDIA 595.58)
- Hide HWE Kernel card entirely when value is null (Stable stream)

Assisted-by: Claude Sonnet 4.6 via OpenCode
Add a gitignore exception and empty seed file so the Docusaurus Webpack
bundler can resolve the @site/static/data/sbom-attestations.json import
even when the GHA data cache is cold (first build on a new branch or
after a cache eviction).

The real data is populated by the update-sbom-cache workflow; this seed
acts as a fallback so builds don't fail with 'Cannot find module' when
the cache hasn't been warmed yet.

Assisted-by: Claude Sonnet 4.6 via GitHub Copilot
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces visual and functional enhancements to the Driver Versions Catalog, adding version tracking and bump indicators for GNOME and Mesa. It refactors the Images Catalog to use Docusaurus CodeBlock components for command snippets and improves accessibility by ensuring copy buttons are visible on keyboard focus. Additionally, the SBOM attestation logic was simplified and a placeholder data file was added. A review comment identified significant indentation inconsistencies in the ImagesCatalog.tsx file that should be addressed to maintain code quality.

Comment on lines +205 to 461
const tone =
product.artwork === "dakotaraptor"
? styles.cardDakota
: product.artwork === "achillobator"
? styles.cardLts
: styles.cardBluefin;
const digestShort = product.metadata?.digestShort || "Unavailable";
const digestFull = product.metadata?.digest || null;
const digestLink = product.metadata?.digestLink;
const ostreeShort = product.metadata?.labels?.ostreeCommit?.slice(0, 12);
const releaseUrl = assetsLink(product.versions?.release?.url);
const lastValidated = formatDate(catalog.generatedAt || null);
const lastPublished = formatDate(product.lastPublishedAt || null);
const hasNvidiaVariant =
product.streams.some((entry) => Boolean(entry.nvidiaCommand)) ||
product.testingStreams.some((entry) => Boolean(entry.nvidiaCommand));
const nvidiaEnabled = Boolean(nvidiaModeByProduct[product.id]);

return (
<article key={product.id} className={`${styles.card} ${tone}`}>
<header className={styles.cardHeader}>
<Heading as="h2" className={styles.cardTitle}>
{product.name}
</Heading>
<span className={styles.registryBadge}>{product.org}</span>
</header>
return (
<article key={product.id} className={`${styles.card} ${tone}`}>
<header className={styles.cardHeader}>
<Heading as="h2" className={styles.cardTitle}>
{product.name}
</Heading>
<span className={styles.registryBadge}>{product.org}</span>
</header>

<section className={styles.linkRow}>
<Link
to={product.packagePageUrl}
target="_blank"
rel="noopener noreferrer"
>
Package Page
</Link>
{product.isoSectionLink && (
<>
<span>·</span>
<Link to={product.isoSectionLink}>Download ISO</Link>
</>
)}
{releaseUrl && (
<>
<span>·</span>
<Link
to={releaseUrl}
target="_blank"
rel="noopener noreferrer"
>
Release Assets
</Link>
</>
)}
{digestLink ? (
<>
<span>·</span>
<Link
to={digestLink}
target="_blank"
rel="noopener noreferrer"
title={digestFull || digestShort}
>
Digest {digestShort}
</Link>
</>
) : (
<>
<span>·</span>
<span>Digest {digestShort}</span>
</>
)}
{ostreeShort && (
<>
<span>·</span>
<span>OSTree {ostreeShort}</span>
</>
)}
</section>
<section className={styles.linkRow}>
<Link to={product.packagePageUrl} target="_blank" rel="noopener noreferrer">
Package Page
</Link>
{product.isoSectionLink && (
<>
<span>·</span>
<Link to={product.isoSectionLink}>Download ISO</Link>
</>
)}
{releaseUrl && (
<>
<span>·</span>
<Link to={releaseUrl} target="_blank" rel="noopener noreferrer">
Release Assets
</Link>
</>
)}
{digestLink ? (
<>
<span>·</span>
<Link
to={digestLink}
target="_blank"
rel="noopener noreferrer"
title={digestFull || digestShort}
>
Digest {digestShort}
</Link>
</>
) : (
<>
<span>·</span>
<span>Digest {digestShort}</span>
</>
)}
{ostreeShort && (
<>
<span>·</span>
<span>OSTree {ostreeShort}</span>
</>
)}
</section>

<p className={styles.summary}>{product.summary}</p>
<p className={styles.summary}>{product.summary}</p>

<div className={styles.statsRow}>
<span className={styles.statChip}>
<strong>Pulls:</strong> {product.downloads.display}
</span>
<span className={sourceClass(product.downloads.source)}>
{sourceText(product.downloads.source, "Downloads")}
</span>
<span className={sourceClass(product.metadataSource)}>
{sourceText(product.metadataSource, "Metadata")}
</span>
</div>
<div className={styles.statsRow}>
<span className={styles.statChip}>
<strong>Pulls:</strong> {product.downloads.display}
</span>
<span className={sourceClass(product.downloads.source)}>
{sourceText(product.downloads.source, "Downloads")}
</span>
<span className={sourceClass(product.metadataSource)}>
{sourceText(product.metadataSource, "Metadata")}
</span>
</div>

<p className={styles.validationMeta}>
Last validated: <strong>{lastValidated}</strong> · Last published:{" "}
<strong>{lastPublished}</strong>
</p>
<p className={styles.validationMeta}>
Last validated: <strong>{lastValidated}</strong> · Last published: <strong>{lastPublished}</strong>
</p>

<section
className={`${styles.section} ${styles.focusSection} ${styles.streamsSection}`}
>
<div className={styles.sectionHeader}>
<Heading as="h3" className={styles.sectionTitle}>
Streams
</Heading>
{hasNvidiaVariant && (
<div className={styles.nvidiaControl}>
<p className={styles.nvidiaToggleLabel}>Graphics Drivers</p>
<p className={styles.nvidiaToggleQuestion}>
Add Nvidia driver?
</p>
<div
className={styles.nvidiaToggleGroup}
role="group"
aria-label="Nvidia driver toggle"
<section className={`${styles.section} ${styles.focusSection} ${styles.streamsSection}`}>
<div className={styles.sectionHeader}>
<Heading as="h3" className={styles.sectionTitle}>
Streams
</Heading>
{hasNvidiaVariant && (
<div className={styles.nvidiaControl}>
<p className={styles.nvidiaToggleLabel}>Graphics Drivers</p>
<p className={styles.nvidiaToggleQuestion}>Add Nvidia driver?</p>
<div className={styles.nvidiaToggleGroup} role="group" aria-label="Nvidia driver toggle">
<button
type="button"
className={`button button--sm ${!nvidiaEnabled ? "button--primary" : "button--secondary"}`}
aria-pressed={!nvidiaEnabled}
onClick={() =>
setNvidiaModeByProduct((current) => ({
...current,
[product.id]: false,
}))
}
>
No
</button>
<button
type="button"
className={`button button--sm ${nvidiaEnabled ? "button--primary" : "button--secondary"}`}
aria-pressed={nvidiaEnabled}
onClick={() =>
setNvidiaModeByProduct((current) => ({
...current,
[product.id]: true,
}))
}
>
<button
type="button"
className={`button button--sm ${!nvidiaEnabled ? "button--primary" : "button--secondary"}`}
aria-pressed={!nvidiaEnabled}
onClick={() =>
setNvidiaModeByProduct((current) => ({
...current,
[product.id]: false,
}))
}
>
No
</button>
<button
type="button"
className={`button button--sm ${nvidiaEnabled ? "button--primary" : "button--secondary"}`}
aria-pressed={nvidiaEnabled}
onClick={() =>
setNvidiaModeByProduct((current) => ({
...current,
[product.id]: true,
}))
}
>
Yes
</button>
</div>
Yes
</button>
</div>
)}
</div>
</div>
)}
</div>

{product.streams.length > 0 ? (
<Tabs
groupId={`streams-${product.id}`}
values={product.streams.map((entry) => ({
label: entry.label,
value: tabValue(entry.tag),
}))}
>
{product.streams.map((entry) => (
<TabItem key={entry.tag} value={tabValue(entry.tag)}>
<p className={styles.tabCopy}>
Use this command to switch to the{" "}
<strong>{entry.label.toLowerCase()}</strong> channel for
this image. It is the quickest way to stay on that
release stream.
</p>
{nvidiaEnabled ? (
entry.nvidiaCommand ? (
<>
<div className={styles.streamVersionPills}>
<span className={styles.versionPill}>
<strong>GNOME</strong>{" "}
{entry.versions?.gnome || "Unknown"}
</span>
<span className={styles.versionPill}>
<strong>Linux</strong>{" "}
{entry.versions?.kernel || "Unknown"}
</span>
<span className={styles.versionPill}>
<strong>NVIDIA</strong>{" "}
{entry.versions?.nvidia || "Unknown"}
</span>
</div>
<code>{entry.nvidiaCommand}</code>
</>
) : (
<p className={styles.emptyText}>
No Nvidia variant published for this stream tag.
</p>
)
) : (
{product.streams.length > 0 ? (
<Tabs
groupId={`streams-${product.id}`}
values={product.streams.map((entry) => ({
label: entry.label,
value: tabValue(entry.tag),
}))}
>
{product.streams.map((entry) => (
<TabItem key={entry.tag} value={tabValue(entry.tag)}>
<p className={styles.tabCopy}>
Use this command to switch to the <strong>{entry.label.toLowerCase()}</strong> channel for this image.
It is the quickest way to stay on that release stream.
</p>
{nvidiaEnabled ? (
entry.nvidiaCommand ? (
<>
<div className={styles.streamVersionPills}>
<span className={styles.versionPill}>
<strong>GNOME</strong>{" "}
{entry.versions?.gnome || "Unknown"}
<strong>GNOME</strong> {entry.versions?.gnome || "Unknown"}
</span>
<span className={styles.versionPill}>
<strong>Linux</strong> {entry.versions?.kernel || "Unknown"}
</span>
<span className={styles.versionPill}>
<strong>Linux</strong>{" "}
{entry.versions?.kernel || "Unknown"}
<strong>NVIDIA</strong> {entry.versions?.nvidia || "Unknown"}
</span>
{entry.versions?.nvidia && (
<span className={styles.versionPill}>
<strong>NVIDIA</strong> {entry.versions.nvidia}
</span>
)}
</div>
<code>{entry.command}</code>
<CodeBlock language="bash">{entry.nvidiaCommand}</CodeBlock>
</>
)}
</TabItem>
))}
</Tabs>
) : (
<p className={styles.emptyText}>No active tags.</p>
)}
) : (
<p className={styles.emptyText}>No Nvidia variant published for this stream tag.</p>
)
) : (
<>
<div className={styles.streamVersionPills}>
<span className={styles.versionPill}>
<strong>GNOME</strong> {entry.versions?.gnome || "Unknown"}
</span>
<span className={styles.versionPill}>
<strong>Linux</strong> {entry.versions?.kernel || "Unknown"}
</span>
{entry.versions?.nvidia && (
<span className={styles.versionPill}>
<strong>NVIDIA</strong> {entry.versions.nvidia}
</span>
)}
</div>
<CodeBlock language="bash">{entry.command}</CodeBlock>
</>
)}
</TabItem>
))}
</Tabs>
) : (
<p className={styles.emptyText}>No active tags.</p>
)}

<details className={styles.testingDetails}>
<summary>
Testing Branches ({product.testingStreams.length})
</summary>
<StreamList
streams={product.testingStreams}
preferNvidia={nvidiaEnabled}
/>
</details>
</section>
<details className={styles.testingDetails}>
<summary>Testing Branches ({product.testingStreams.length})</summary>
<StreamList streams={product.testingStreams} preferNvidia={nvidiaEnabled} />
</details>
</section>

<section
className={`${styles.section} ${styles.focusSection} ${styles.securitySection}`}
<section className={`${styles.section} ${styles.focusSection} ${styles.securitySection}`}>
<Heading as="h3" className={styles.sectionTitle}>
Signing and SBOM
</Heading>
{product.security?.cosignKeyUrl ? (
<p className={styles.securityText}>
Key: <code>{product.security.cosignKeyUrl}</code>
</p>
) : (
<p className={styles.securityText}>No published cosign key URL in this catalog.</p>
)}

<Tabs
groupId={`security-${product.id}`}
values={[
{ label: "Verify Signature", value: "verify-signature" },
{ label: "Verify Provenance", value: "verify-provenance" },
{ label: "Generate SBOM", value: "generate-sbom" },
]}
>
<Heading as="h3" className={styles.sectionTitle}>
Signing and SBOM
</Heading>
{product.security?.cosignKeyUrl ? (
<p className={styles.securityText}>
Key: <code>{product.security.cosignKeyUrl}</code>
<TabItem value="verify-signature">
<p className={styles.tabCopy}>
Signature verification confirms this image was signed by the expected maintainers and helps detect tampering before deployment.
{" "}
<Link to="https://docs.sigstore.dev/cosign/verifying/verify/" target="_blank" rel="noopener noreferrer">
Learn more
</Link>
.
</p>
) : (
<p className={styles.securityText}>
No published cosign key URL in this catalog.
{product.security?.verifyCommand && <CodeBlock language="bash">{product.security.verifyCommand}</CodeBlock>}
</TabItem>
<TabItem value="verify-provenance">
<p className={styles.tabCopy}>
Provenance attestation lets you validate how the image was built in CI so you can make trust decisions from evidence.
{" "}
<Link to="https://slsa.dev/" target="_blank" rel="noopener noreferrer">
Learn more
</Link>
.
</p>
)}

<Tabs
groupId={`security-${product.id}`}
values={[
{ label: "Verify Signature", value: "verify-signature" },
{ label: "Verify Provenance", value: "verify-provenance" },
{ label: "Generate SBOM", value: "generate-sbom" },
]}
>
<TabItem value="verify-signature">
{product.security?.attestCommand && <CodeBlock language="bash">{product.security.attestCommand}</CodeBlock>}
{product.security?.attestCommand && product.security.hasAttestation === false && (
<p className={styles.tabCopy}>
Signature verification confirms this image was signed by the
expected maintainers and helps detect tampering before
deployment.{" "}
<Link
to="https://docs.sigstore.dev/cosign/verifying/verify/"
target="_blank"
rel="noopener noreferrer"
>
Learn more
</Link>
.
Note: attestations are not yet published for this image. The command is provided for when they are.
</p>
{product.security?.verifyCommand && (
<code>{product.security.verifyCommand}</code>
)}
</TabItem>
<TabItem value="verify-provenance">
<p className={styles.tabCopy}>
Provenance attestation lets you validate how the image was
built in CI so you can make trust decisions from evidence.{" "}
<Link
to="https://slsa.dev/"
target="_blank"
rel="noopener noreferrer"
>
Learn more
</Link>
.
</p>
{latestAttestation && (
<p className={styles.tabCopy}>
{latestAttestation.attestation.verified
? `Latest release (${latestAttestation.tag}): attestation verified.`
: latestAttestation.attestation.present
? `Latest release (${latestAttestation.tag}): attestation present but verification failed.`
: `Latest release (${latestAttestation.tag}): no attestation found.`}
</p>
)}
{product.security?.attestCommand && (
<code>{product.security.attestCommand}</code>
)}
{product.security?.attestCommand &&
product.security.hasAttestation === false &&
!latestAttestation?.attestation.present && (
<p className={styles.tabCopy}>
Note: attestations are not yet published for this image.
The command is provided for when they are.
</p>
)}
</TabItem>
<TabItem value="generate-sbom">
<p className={styles.tabCopy}>
SBOM generation gives you a component inventory for audits,
policy checks, and vulnerability triage workflows.{" "}
<Link
to="https://github.com/anchore/syft"
target="_blank"
rel="noopener noreferrer"
>
Learn more
</Link>
.
</p>
{product.security?.sbomCommand && (
<code>{product.security.sbomCommand}</code>
)}
</TabItem>
</Tabs>
</section>
</article>
);
)}
</TabItem>
<TabItem value="generate-sbom">
<p className={styles.tabCopy}>
SBOM generation gives you a component inventory for audits, policy checks, and vulnerability triage workflows.
{" "}
<Link to="https://github.com/anchore/syft" target="_blank" rel="noopener noreferrer">
Learn more
</Link>
.
</p>
{product.security?.sbomCommand && <CodeBlock language="bash">{product.security.sbomCommand}</CodeBlock>}
</TabItem>
</Tabs>
</section>
</article>
);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The entire renderCards map function has pervasive indentation issues. The block starting here is indented by 6 spaces instead of the 8 spaces required to be consistent with the 2-space indentation used throughout the rest of the project. This significantly impacts the readability and maintainability of the component.

The JSON type inferred by TypeScript from the seed file does not have
a 'gnome' field in the versions object, causing the direct 'as DriverCatalog'
cast to fail with TS2352. Use 'as unknown as DriverCatalog' to bridge
the type gap — the runtime shape is validated by the component logic.

Assisted-by: Claude Sonnet 4.6 via GitHub Copilot
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castrojo castrojo merged commit 6319f8f into projectbluefin:main Mar 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant