diff --git a/content/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs.md b/content/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs.md
index 568724685381..06f54ce4c288 100644
--- a/content/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs.md
+++ b/content/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs.md
@@ -23,8 +23,8 @@ redirect_from:
job1:
runs-on: ubuntu-latest
outputs:
- output1: ${{ steps.step1.outputs.test }}
- output2: ${{ steps.step2.outputs.test }}
+ output1: {% raw %}${{ steps.step1.outputs.test }}{% endraw %}
+ output2: {% raw %}${{ steps.step2.outputs.test }}{% endraw %}
steps:
- id: step1
run: echo "test=hello" >> "$GITHUB_OUTPUT"
@@ -52,8 +52,8 @@ redirect_from:
needs: job1
steps:
- env:
- OUTPUT1: ${{needs.job1.outputs.output1}}
- OUTPUT2: ${{needs.job1.outputs.output2}}
+ OUTPUT1: {% raw %}${{needs.job1.outputs.output1}}{% endraw %}
+ OUTPUT2: {% raw %}${{needs.job1.outputs.output2}}{% endraw %}
run: echo "$OUTPUT1 $OUTPUT2"
```
diff --git a/content/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors.md b/content/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors.md
index 67409e16d30a..396864b3f4bf 100644
--- a/content/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors.md
+++ b/content/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors.md
@@ -17,6 +17,11 @@ shortTitle: Contributor guidelines
To help your project contributors do good work, you can add a file with contribution guidelines to your project repository's root, `docs`, or `.github` folder. When someone opens a pull request or creates an issue, they will see a link to that file. {% ifversion fpt or ghec %}The link to the contributing guidelines also appears on your repository's `contribute` page. For an example of a `contribute` page, see [github/docs/contribute](https://github.com/github/docs/contribute).{% endif %}
+{% ifversion fpt or ghec or ghes > 3.18 %}If your repository includes a `CONTRIBUTING.md` file, {% data variables.product.github %} also surfaces it in two other places to make it easier for contributors to discover:
+
+* A "{% octicon "people" aria-hidden="true" aria-label="people" %} Contributing" tab in the repository overview (next to the "{% octicon "book" aria-hidden="true" aria-label="book" %} README" and "{% octicon "code-of-conduct" aria-hidden="true" aria-label="code-of-conduct" %} Code of conduct")
+* A "Contributing" link in the repository sidebar{% endif %}
+
For the repository owner, contribution guidelines are a way to communicate how people should contribute.
For contributors, the guidelines help them verify that they're submitting well-formed pull requests and opening useful issues.
@@ -28,7 +33,7 @@ You can create default contribution guidelines for your organization or personal
> [!TIP]
> Repository maintainers can set specific guidelines for issues by creating an issue or pull request template for the repository. For more information, see [AUTOTITLE](/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates).
-## Adding a _CONTRIBUTING_ file
+## Adding a `CONTRIBUTING.md` file
{% data reusables.repositories.navigate-to-repo %}
{% data reusables.files.add-file %}
diff --git a/content/contributing/writing-for-github-docs/using-yaml-frontmatter.md b/content/contributing/writing-for-github-docs/using-yaml-frontmatter.md
index 59fcf89afd41..5e71b83b9a5e 100644
--- a/content/contributing/writing-for-github-docs/using-yaml-frontmatter.md
+++ b/content/contributing/writing-for-github-docs/using-yaml-frontmatter.md
@@ -102,7 +102,7 @@ For more information, see [AUTOTITLE](/contributing/syntax-and-versioning-for-gi
* Purpose: Set a human-friendly title for use in the rendered page's `
` tag and an `h1` element at the top of the page.
* Type: `String`
-* Optional. If omitted, the page `` will still be set, albeit with a generic value like `GitHub.com` or `GitHub Enterprise`.
+* **Required**.
### `shortTitle`
diff --git a/data/ui.yml b/data/ui.yml
index 319655cefa7e..d749d965157a 100644
--- a/data/ui.yml
+++ b/data/ui.yml
@@ -260,6 +260,17 @@ footer:
expert_services: Expert services
blog: Blog
machine: Some of this content may be machine- or AI-translated.
+bespoke_landing:
+ articles: Articles
+ all_categories: All categories
+ search_articles: Search articles
+discovery_landing:
+ recommended: Recommended
+ articles: Articles
+ all_categories: All categories
+ search_articles: Search articles
+journey_landing:
+ articles: '{{ number }} Articles'
product_landing:
quickstart: Quickstart
reference: Reference
diff --git a/src/fixtures/fixtures/data/ui.yml b/src/fixtures/fixtures/data/ui.yml
index 319655cefa7e..d749d965157a 100644
--- a/src/fixtures/fixtures/data/ui.yml
+++ b/src/fixtures/fixtures/data/ui.yml
@@ -260,6 +260,17 @@ footer:
expert_services: Expert services
blog: Blog
machine: Some of this content may be machine- or AI-translated.
+bespoke_landing:
+ articles: Articles
+ all_categories: All categories
+ search_articles: Search articles
+discovery_landing:
+ recommended: Recommended
+ articles: Articles
+ all_categories: All categories
+ search_articles: Search articles
+journey_landing:
+ articles: '{{ number }} Articles'
product_landing:
quickstart: Quickstart
reference: Reference
diff --git a/src/frame/components/ui/ScrollButton/ScrollButton.tsx b/src/frame/components/ui/ScrollButton/ScrollButton.tsx
index 59149ebde57e..6cce1a4e62a1 100644
--- a/src/frame/components/ui/ScrollButton/ScrollButton.tsx
+++ b/src/frame/components/ui/ScrollButton/ScrollButton.tsx
@@ -18,6 +18,14 @@ export const ScrollButton = ({ className, ariaLabel }: ScrollButtonPropsT) => {
// We cannot determine document.documentElement.scrollTop height because we set the height: 100vh and set overflow to auto to keep the header sticky
// That means window.scrollTop height is always 0
// Using IntersectionObserver we can determine if the h1 header is in view or not. If not, we show the scroll to top button, if so, we hide it
+ const h1Element = document.getElementsByTagName('h1')[0]
+ if (!h1Element) {
+ if (process.env.NODE_ENV !== 'production') {
+ throw new Error('No h1 element found in the document.')
+ }
+ return
+ }
+
const observer = new IntersectionObserver(
function (entries) {
if (entries[0].isIntersecting === false) {
@@ -28,7 +36,7 @@ export const ScrollButton = ({ className, ariaLabel }: ScrollButtonPropsT) => {
},
{ threshold: [0] },
)
- observer.observe(document.getElementsByTagName('h1')[0])
+ observer.observe(h1Element)
return () => {
observer.disconnect()
}
diff --git a/src/frame/lib/frontmatter.js b/src/frame/lib/frontmatter.js
index 1483278ec3f7..013fe4482c32 100644
--- a/src/frame/lib/frontmatter.js
+++ b/src/frame/lib/frontmatter.js
@@ -14,6 +14,9 @@ const layoutNames = [
'release-notes',
'inline',
'category-landing',
+ 'bespoke-landing',
+ 'discovery-landing',
+ 'journey-landing',
false,
]
@@ -325,6 +328,11 @@ export const schema = {
},
description: 'Array of articles to feature in the spotlight section',
},
+ // Recommended configuration for category landing pages
+ recommended: {
+ type: 'array',
+ description: 'Array of articles to feature in the carousel section',
+ },
},
}
diff --git a/src/frame/middleware/context/generic-toc.ts b/src/frame/middleware/context/generic-toc.ts
index c15a74eaa146..948a2d712f51 100644
--- a/src/frame/middleware/context/generic-toc.ts
+++ b/src/frame/middleware/context/generic-toc.ts
@@ -3,6 +3,24 @@ import type { Response, NextFunction } from 'express'
import type { ExtendedRequest, Context, Tree, ToC } from '@/types'
import findPageInSiteTree from '@/frame/lib/find-page-in-site-tree'
+function isNewLandingPage(currentLayoutName: string): boolean {
+ return (
+ currentLayoutName === 'category-landing' ||
+ currentLayoutName === 'bespoke_landing' ||
+ currentLayoutName === 'discovery_landing' ||
+ currentLayoutName === 'journey_landing'
+ )
+}
+
+// TODO: TEMP: This is a temporary solution to turn off/on new landing pages while we develop them.
+function isNewLandingPageFeature(req: ExtendedRequest): boolean {
+ return (
+ req.query?.feature === 'bespoke-landing' ||
+ req.query?.feature === 'journey-landing' ||
+ req.query?.feature === 'discovery-landing'
+ )
+}
+
// This module adds either flatTocItems or nestedTocItems to the context object for
// product, category, and subcategory TOCs that don't have other layouts specified.
// They are rendered by includes/generic-toc-flat.html or includes/generic-toc-nested.html.
@@ -10,8 +28,9 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
if (!req.context) throw new Error('request not contextualized')
if (!req.context.page) return next()
if (
+ !isNewLandingPageFeature(req) &&
req.context.currentLayoutName !== 'default' &&
- req.context.currentLayoutName !== 'category-landing'
+ !isNewLandingPage(req.context.currentLayoutName || '')
)
return next()
// This middleware can only run on product, category, and subcategories.
@@ -96,7 +115,10 @@ export default async function genericToc(req: ExtendedRequest, res: Response, ne
renderIntros = false
req.context.genericTocNested = await getTocItems(treePage, req.context, {
recurse: isRecursive,
- renderIntros: req.context.currentLayoutName === 'category-landing' ? true : false,
+ renderIntros:
+ isNewLandingPageFeature(req) || isNewLandingPage(req.context.currentLayoutName || '')
+ ? true
+ : false,
includeHidden,
})
}
diff --git a/src/landings/components/bespoke/BespokeLanding.tsx b/src/landings/components/bespoke/BespokeLanding.tsx
new file mode 100644
index 000000000000..29746737c2dc
--- /dev/null
+++ b/src/landings/components/bespoke/BespokeLanding.tsx
@@ -0,0 +1,29 @@
+import { useMemo } from 'react'
+
+import { DefaultLayout } from '@/frame/components/DefaultLayout'
+import { useBespokeContext } from '@/landings/context/BespokeContext'
+import { LandingHero } from '@/landings/components/shared/LandingHero'
+import { ArticleGrid } from '@/landings/components/shared/LandingArticleGridWithFilter'
+
+import type { ArticleCardItems } from '@/landings/types'
+
+export const BespokeLanding = () => {
+ const { title, intro, tocItems } = useBespokeContext()
+
+ const flatArticles: ArticleCardItems = useMemo(
+ () => tocItems.flatMap((item) => item.childTocItems || []),
+ [tocItems],
+ )
+
+ return (
+
+