diff --git a/src/components/docs-version-switcher/index.tsx b/src/components/docs-version-switcher/index.tsx
index b29cf5aada..01fea84045 100644
--- a/src/components/docs-version-switcher/index.tsx
+++ b/src/components/docs-version-switcher/index.tsx
@@ -73,6 +73,12 @@ const DocsVersionSwitcher = ({
selectedOption = options.find(
(option: VersionSelectItem) => option.isLatest === true
)
+ // In some edge cases, there may be no latest version, such as for
+ // versioned docs that no longer exist in the latest version. For these
+ // cases, fallback to selecting the first option.
+ if (!selectedOption) {
+ selectedOption = options[0]
+ }
}
const projectNameForLabel = setProjectForAriaLabel(
diff --git a/src/components/truncate-max-lines/index.tsx b/src/components/truncate-max-lines/index.tsx
index 77cdd0e521..09fcf54b1d 100644
--- a/src/components/truncate-max-lines/index.tsx
+++ b/src/components/truncate-max-lines/index.tsx
@@ -10,7 +10,6 @@ import s from './truncate-max-lines.module.css'
function TruncateMaxLines({
children,
className,
- lineHeight,
maxLines,
}: TruncateMaxLinesProps) {
return (
@@ -19,7 +18,6 @@ function TruncateMaxLines({
style={
{
'--max-lines': maxLines,
- '--line-height': lineHeight,
} as React.CSSProperties
}
>
diff --git a/src/components/truncate-max-lines/truncate-max-lines.module.css b/src/components/truncate-max-lines/truncate-max-lines.module.css
index 28862ac4be..5cd039c24e 100644
--- a/src/components/truncate-max-lines/truncate-max-lines.module.css
+++ b/src/components/truncate-max-lines/truncate-max-lines.module.css
@@ -6,14 +6,11 @@
.root {
/**
*
- * Expects properties to be set in JS (or in parent CSS):
+ * Expects property to be set in JS (or in parent CSS):
*
- * var(--line-height): a unitless expression of line height
* var(--max-lines): a number of lines to show before truncating
*/
- /** Both variables are unitless, so we multiply by 1em for "x lines high" */
- max-height: calc(var(--line-height) * var(--max-lines) * 1em);
overflow: hidden;
text-overflow: ellipsis;
diff --git a/src/components/truncate-max-lines/types.ts b/src/components/truncate-max-lines/types.ts
index 2bbaa5ee79..16e833e712 100644
--- a/src/components/truncate-max-lines/types.ts
+++ b/src/components/truncate-max-lines/types.ts
@@ -8,6 +8,5 @@ import type { ReactNode } from 'react'
export interface TruncateMaxLinesProps {
children: ReactNode
className?: string
- lineHeight: string
maxLines: number
}
diff --git a/src/components/tutorial-collection-cards/components/card-body/index.tsx b/src/components/tutorial-collection-cards/components/card-body/index.tsx
index 43b15d8dfe..308bd3a870 100644
--- a/src/components/tutorial-collection-cards/components/card-body/index.tsx
+++ b/src/components/tutorial-collection-cards/components/card-body/index.tsx
@@ -9,12 +9,7 @@ import s from './card-body.module.css'
function CardBody({ text }: { text: string }) {
return (
-
- {text}
-
+ {text}
)
}
diff --git a/src/lib/rehype-code-plugins.ts b/src/lib/rehype-code-plugins.ts
index 9f128b7f03..73e48bf38c 100644
--- a/src/lib/rehype-code-plugins.ts
+++ b/src/lib/rehype-code-plugins.ts
@@ -76,6 +76,7 @@ export const rehypeCodePlugins: Pluggable[] = [
break
case 'log':
case 'plain-text':
+ case 'plaintext':
case 'ebnf':
case 'rego':
options.lang = 'text'
diff --git a/src/views/docs-view/server.ts b/src/views/docs-view/server.ts
index 51ad18d38e..84424c41ed 100644
--- a/src/views/docs-view/server.ts
+++ b/src/views/docs-view/server.ts
@@ -36,6 +36,7 @@ import {
import tutorialMap from 'data/_tutorial-map.generated.json'
// Local imports
+import { getValidVersions } from './utils/get-valid-versions'
import { getProductUrlAdjuster } from './utils/product-url-adjusters'
import { getBackToLink } from './utils/get-back-to-link'
import { getDeployPreviewLoader } from './utils/get-deploy-preview-loader'
@@ -384,6 +385,21 @@ export function getStaticGenerationFunctions<
mainWidth: isDocsLanding ? 'wide' : 'narrow',
}
+ /**
+ * Filter versions to include only those where this document exists
+ */
+ // Construct a document path that the content API will recognize
+ const pathWithoutVersion = pathParts
+ .filter((part) => part !== versionPathPart)
+ .join('/')
+ const fullPath = `doc#${path.join(basePathForLoader, pathWithoutVersion)}`
+ // Filter for valid versions, fetching from the content API under the hood
+ const validVersions = await getValidVersions(
+ versions,
+ fullPath,
+ productSlugForLoader
+ )
+
/**
* Determine whether to show the version selector
*
@@ -392,8 +408,8 @@ export function getStaticGenerationFunctions<
* (We use `v0.0.x` as a placeholder version for un-versioned documentation)
*/
const hasMeaningfulVersions =
- versions.length > 0 &&
- (versions.length > 1 || versions[0].version !== 'v0.0.x')
+ validVersions.length > 0 &&
+ (validVersions.length > 1 || validVersions[0].version !== 'v0.0.x')
/**
* We want to show "Edit on GitHub" links for public content repos only.
@@ -440,7 +456,7 @@ export function getStaticGenerationFunctions<
!hideVersionSelector &&
!isReleaseNotesPage(currentPathUnderProduct) && // toggle version dropdown
hasMeaningfulVersions
- ? versions
+ ? validVersions
: null,
}
diff --git a/src/views/docs-view/utils/get-valid-versions.ts b/src/views/docs-view/utils/get-valid-versions.ts
new file mode 100644
index 0000000000..829c93aa4a
--- /dev/null
+++ b/src/views/docs-view/utils/get-valid-versions.ts
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+// Types
+import type { VersionSelectItem } from '../loaders/remote-content'
+
+const CONTENT_API_URL = process.env.MKTG_CONTENT_API
+const VERSIONS_ENDPOINT = '/api/content-versions'
+
+/**
+ * Given a list of all possible versions, as well as a document path and
+ * content repo identifier for our content API,
+ * Return a filter list of versions that includes only those versions
+ * where this document exists.
+ *
+ * To determine in which versions this document exists, we make a request
+ * to a content API that returns a list of strings representing known versions.
+ * We use this to filter out unknown versions from our incoming version list.
+ */
+export async function getValidVersions(
+ /**
+ * An array of version select items representing all possible versions for
+ * the content source repository in question (`productSlugForLoader`).
+ * May be undefined or empty if versioned docs are not enabled, for example
+ * during local preview.
+ */
+ versions: VersionSelectItem[],
+ /**
+ * A identifier for the document, consumable by our content API.
+ * For markdown documents, this is `doc#` followed by the full path of the
+ * document within the content source repository.
+ */
+ fullPath: string,
+ /**
+ * The product slug for the document, consumable by our content API.
+ * The naming here is difficult, as the actual function here is to identify
+ * specific content source repositories. These are often but not always
+ * product slugs. For example Terraform has multiple content source repos
+ * for different parts of the product.
+ */
+ productSlugForLoader: string
+): Promise {
+ // If versions are falsy or empty, we can skip the API calls and return []
+ if (!versions || versions.length === 0) return []
+ try {
+ // Build the URL to fetch known versions of this document
+ const validVersionsUrl = new URL(VERSIONS_ENDPOINT, CONTENT_API_URL)
+ validVersionsUrl.searchParams.set('product', productSlugForLoader)
+ validVersionsUrl.searchParams.set('fullPath', fullPath)
+ // Fetch known versions of this document
+ const response = await fetch(validVersionsUrl.toString())
+ const { versions: knownVersions } = await response.json()
+ // Apply the filter, and return the valid versions
+ return versions.filter((option) => knownVersions.includes(option.version))
+ } catch (error) {
+ console.error(
+ `[docs-view/server] error fetching known versions for "${productSlugForLoader}" document "${fullPath}". Falling back to showing all versions.`,
+ error
+ )
+ return versions
+ }
+}
diff --git a/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css b/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css
index 48ec3ca61f..e32ed6de77 100644
--- a/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css
+++ b/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css
@@ -27,7 +27,6 @@
right: 0;
top: var(--graphic-position-top);
overflow: hidden;
- width: fit-content;
margin: 0 auto;
pointer-events: none;
}
diff --git a/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css b/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css
index 11f2fbc0a2..e3e14381bd 100644
--- a/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css
+++ b/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css
@@ -6,9 +6,7 @@
.hcpCard {
background-color: var(--token-color-foreground-strong);
background-image: url('../../img/hcp-graphic.svg');
- background-position: center top;
background-repeat: no-repeat;
- background-size: cover;
border: none;
gap: 50px;
padding-top: 88px;
diff --git a/src/views/homepage/components/featured-content-grid/search-featured-card.module.css b/src/views/homepage/components/featured-content-grid/search-featured-card.module.css
index a358026003..10d891cb9b 100644
--- a/src/views/homepage/components/featured-content-grid/search-featured-card.module.css
+++ b/src/views/homepage/components/featured-content-grid/search-featured-card.module.css
@@ -9,7 +9,6 @@
background-color: var(--token-color-foreground-strong);
background-position: center top;
background-repeat: no-repeat;
- background-size: cover;
border-radius: 10px;
padding-bottom: 18px;
padding-left: 24px;
@@ -19,7 +18,6 @@
/* 500px with 16px base font */
@media (min-width: 31.25rem) {
background-image: url('../../img/search-card-graphic.svg');
- background-position-x: 1rem;
}
@nest html[data-theme='dark'] & {
diff --git a/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css b/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css
index 887c8330e1..e0406f0a9a 100644
--- a/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css
+++ b/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css
@@ -18,7 +18,7 @@
left: 0;
position: absolute;
right: 0;
- top: -25px;
+ top: 0;
width: 100%;
pointer-events: none;
}
diff --git a/src/views/homepage/components/page-title/page-title.module.css b/src/views/homepage/components/page-title/page-title.module.css
index f22d07df8e..235d0fbd99 100644
--- a/src/views/homepage/components/page-title/page-title.module.css
+++ b/src/views/homepage/components/page-title/page-title.module.css
@@ -32,12 +32,15 @@
--bg-gradient-base: var(--token-color-foreground-high-contrast);
background: linear-gradient(
- 90deg,
- rgba(136, 143, 255, 0.46) 9.03%,
- rgba(223, 255, 199, 0.57) 41.12%,
- rgba(255, 177, 80, 0.31) 61.68%
- ),
- linear-gradient(var(--bg-gradient-base), var(--bg-gradient-base));
+ 78.87deg,
+ #b0ffff 2.19%,
+ #8fd0ff 16.24%,
+ #ddbfff 31.62%,
+ #ffaed0 53.79%,
+ #ffbeaf 67.39%,
+ #fff8c9 79.68%,
+ #fff 91.59%
+ );
background-clip: text;
color: transparent;
display: block;
diff --git a/src/views/homepage/homepage.module.css b/src/views/homepage/homepage.module.css
index ec0173a311..6b9856fc11 100644
--- a/src/views/homepage/homepage.module.css
+++ b/src/views/homepage/homepage.module.css
@@ -23,10 +23,9 @@
.background {
background-color: var(--token-color-foreground-strong);
background-image: url('./img/background.svg');
- background-position-x: center;
+ background-position-x: right;
background-position-y: top;
background-repeat: no-repeat;
- background-size: cover;
content: '';
height: 1145px;
position: absolute;
@@ -40,7 +39,6 @@
@nest html[data-theme='dark'] & {
background-color: var(--token-color-page-primary);
- background-image: url('./img/background-dark.svg');
}
}
diff --git a/src/views/homepage/img/background-dark.svg b/src/views/homepage/img/background-dark.svg
deleted file mode 100644
index 6b8e9c19bc..0000000000
--- a/src/views/homepage/img/background-dark.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
diff --git a/src/views/homepage/img/background.svg b/src/views/homepage/img/background.svg
index 4c8d3eac35..eb7e4e2b5b 100644
--- a/src/views/homepage/img/background.svg
+++ b/src/views/homepage/img/background.svg
@@ -1,61 +1,128 @@
-