+
+
{children}
diff --git a/apps/site/layouts/Blog.tsx b/apps/site/layouts/Blog.tsx
index 7cf0755dd23a0..cc00cad2b0f3f 100644
--- a/apps/site/layouts/Blog.tsx
+++ b/apps/site/layouts/Blog.tsx
@@ -3,6 +3,7 @@ import { useTranslations } from 'next-intl';
import { getClientContext } from '#site/client-context';
import BlogHeader from '#site/components/Blog/BlogHeader';
import BlogCategories from '#site/components/BlogCategories';
+import CarbonTopBar from '#site/components/CarbonAds/TopBar';
import Footer from '#site/components/Footer';
import NavBar from '#site/components/NavBar';
import { getBlogData } from '#site/util/blog';
@@ -55,6 +56,8 @@ const BlogLayout: FC = () => {
+
+
= ({ children }) => {
{type === 'vulnerability' && }
+
+
{frontmatter.title}
diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs
index b9509bb4c19f3..ff16d1ed200da 100644
--- a/apps/site/next.constants.mjs
+++ b/apps/site/next.constants.mjs
@@ -172,3 +172,14 @@ export const ORAMA_DB_URLS = {
[LEARN_URL.slice(0, -1)]: 'https://nodejs.org/learn/orama-db.json',
[`${DOCS_URL}latest/api`]: 'https://beta.docs.nodejs.org/orama-db.json',
};
+
+/**
+ * The Carbon Ads JSON API endpoint (suffixed with `{zone}.json`)
+ */
+export const CARBON_ADS_URL = 'https://srv.carbonads.net/ads/';
+
+/**
+ * Whether Carbon Ads should count impressions (production only)
+ */
+export const CARBON_ADS_COUNT_IMPRESSIONS =
+ process.env.NEXT_PUBLIC_CARBON_ADS_PRODUCTION === 'true';
diff --git a/apps/site/turbo.json b/apps/site/turbo.json
index bcb3469ba50dc..b26f340ed07df 100644
--- a/apps/site/turbo.json
+++ b/apps/site/turbo.json
@@ -22,6 +22,7 @@
"NEXT_PUBLIC_DOCS_URL",
"NEXT_PUBLIC_BASE_PATH",
"NEXT_PUBLIC_DATA_URL",
+ "NEXT_PUBLIC_CARBON_ADS_PRODUCTION",
"TURBO_CACHE",
"TURBO_TELEMETRY_DISABLED",
"TURBOPACK_STATS"
@@ -53,6 +54,7 @@
"NEXT_PUBLIC_DOCS_URL",
"NEXT_PUBLIC_BASE_PATH",
"NEXT_PUBLIC_DATA_URL",
+ "NEXT_PUBLIC_CARBON_ADS_PRODUCTION",
"TURBO_CACHE",
"TURBO_TELEMETRY_DISABLED",
"TURBOPACK_STATS",
@@ -72,6 +74,7 @@
"NEXT_PUBLIC_DOCS_URL",
"NEXT_PUBLIC_BASE_PATH",
"NEXT_PUBLIC_DATA_URL",
+ "NEXT_PUBLIC_CARBON_ADS_PRODUCTION",
"TURBO_CACHE",
"TURBO_TELEMETRY_DISABLED",
"TURBOPACK_STATS"
@@ -96,6 +99,7 @@
"NEXT_PUBLIC_DOCS_URL",
"NEXT_PUBLIC_BASE_PATH",
"NEXT_PUBLIC_DATA_URL",
+ "NEXT_PUBLIC_CARBON_ADS_PRODUCTION",
"TURBO_CACHE",
"TURBO_TELEMETRY_DISABLED",
"TURBOPACK_STATS"
diff --git a/packages/ui-components/src/Containers/MetaBar/__tests__/index.test.jsx b/packages/ui-components/src/Containers/MetaBar/__tests__/index.test.jsx
index 6460c2fdf125d..ed05df5dd8c69 100644
--- a/packages/ui-components/src/Containers/MetaBar/__tests__/index.test.jsx
+++ b/packages/ui-components/src/Containers/MetaBar/__tests__/index.test.jsx
@@ -60,4 +60,27 @@ describe('MetaBar', () => {
const h6Element = queryByText('Heading Level 6');
assert.ok(!h6Element?.ownerDocument);
});
+
+ it('renders an optional footer after the metadata list', () => {
+ const { container, getByText } = render(
+ Footer content}
+ />
+ );
+
+ const footer = getByText('Footer content');
+
+ assert.equal(container.querySelector('dl').contains(footer), false);
+ assert.equal(container.querySelector('aside').contains(footer), true);
+ });
+
+ it('renders no footer by default', () => {
+ const { container } = render(
+
+ );
+
+ assert.equal(container.querySelector('aside').children.length, 1);
+ });
});
diff --git a/packages/ui-components/src/Containers/MetaBar/index.tsx b/packages/ui-components/src/Containers/MetaBar/index.tsx
index 8f73c84127837..d66304e694a3b 100644
--- a/packages/ui-components/src/Containers/MetaBar/index.tsx
+++ b/packages/ui-components/src/Containers/MetaBar/index.tsx
@@ -4,7 +4,7 @@ import { Fragment, useMemo } from 'react';
import { CODE_LIKE_TYPES } from '#ui/constants';
import type { Heading, LinkLike } from '#ui/types';
-import type { FC, HTMLAttributes } from 'react';
+import type { FC, HTMLAttributes, ReactNode } from 'react';
import styles from './index.module.css';
@@ -16,6 +16,7 @@ type MetaBarProps = {
};
as?: LinkLike;
heading: string;
+ footer?: ReactNode;
} & HTMLAttributes;
const MetaBar: FC = ({
@@ -23,6 +24,7 @@ const MetaBar: FC = ({
headings,
as: Component = 'a',
heading,
+ footer,
...props
}) => {
// The default depth of headings to display in the table of contents.
@@ -74,6 +76,8 @@ const MetaBar: FC = ({
>
)}
+
+ {footer}
);
};