diff --git a/src/landings/components/journey/JourneyLanding.tsx b/src/landings/components/journey/JourneyLanding.tsx
index 7eb489fe0e..2dc85368e7 100644
--- a/src/landings/components/journey/JourneyLanding.tsx
+++ b/src/landings/components/journey/JourneyLanding.tsx
@@ -1,16 +1,105 @@
import { DefaultLayout } from '@/frame/components/DefaultLayout'
import { useLandingContext } from '@/landings/context/LandingContext'
import { LandingHero } from '@/landings/components/shared/LandingHero'
+import { JourneyLearningTracks } from './JourneyLearningTracks'
+
+export type JourneyLearningTrack = {
+ id: string
+ title: string
+ description: string
+ trackName: string
+ trackProduct: string
+ guides?: Array<{
+ href: string
+ title: string
+ }>
+}
export const JourneyLanding = () => {
const { title, intro, heroImage, introLinks } = useLandingContext()
+ // Temp until we hookup real data
+ const stubLearningTracks: JourneyLearningTrack[] = [
+ {
+ id: 'admin:get_started_with_your_enterprise_account',
+ title: 'Get started with your enterprise account',
+ description:
+ 'Set up your enterprise account and configure initial settings for your organization.',
+ trackName: 'get_started_with_your_enterprise_account',
+ trackProduct: 'admin',
+ guides: [
+ {
+ href: '/admin/overview/about-enterprise-accounts?learn=get_started_with_your_enterprise_account&learnProduct=admin',
+ title: 'About enterprise accounts',
+ },
+ {
+ href: '/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/inviting-people-to-manage-your-enterprise?learn=get_started_with_your_enterprise_account&learnProduct=admin',
+ title: 'Inviting people to manage your enterprise',
+ },
+ {
+ href: '/admin/policies/enforcing-policies-for-your-enterprise/about-enterprise-policies?learn=get_started_with_your_enterprise_account&learnProduct=admin',
+ title: 'About enterprise policies',
+ },
+ ],
+ },
+ {
+ id: 'admin:adopting_github_actions_for_your_enterprise_ghec',
+ title: 'Adopt GitHub Actions for your enterprise',
+ description:
+ 'Learn how to plan and implement a rollout of GitHub Actions in your enterprise.',
+ trackName: 'adopting_github_actions_for_your_enterprise_ghec',
+ trackProduct: 'admin',
+ guides: [
+ {
+ href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/about-github-actions-for-enterprises?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin',
+ title: 'About GitHub Actions for enterprises',
+ },
+ {
+ href: '/actions/get-started/understand-github-actions?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin',
+ title: 'Understanding GitHub Actions',
+ },
+ {
+ href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/introducing-github-actions-to-your-enterprise?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin',
+ title: 'Introducing GitHub Actions to your enterprise',
+ },
+ {
+ href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin',
+ title: 'Migrating your enterprise to GitHub Actions',
+ },
+ ],
+ },
+ {
+ id: 'actions:continuous-integration',
+ title: 'Continuous integration with GitHub Actions',
+ description:
+ 'Set up automated testing and building for your projects using GitHub Actions workflows.',
+ trackName: 'continuous-integration',
+ trackProduct: 'actions',
+ guides: [
+ {
+ href: '/actions/automating-builds-and-tests/about-continuous-integration?learn=continuous-integration&learnProduct=actions',
+ title: 'About continuous integration',
+ },
+ {
+ href: '/actions/automating-builds-and-tests/building-and-testing-nodejs?learn=continuous-integration&learnProduct=actions',
+ title: 'Building and testing Node.js',
+ },
+ {
+ href: '/actions/automating-builds-and-tests/building-and-testing-python?learn=continuous-integration&learnProduct=actions',
+ title: 'Building and testing Python',
+ },
+ ],
+ },
+ ]
+
return (
)
diff --git a/src/landings/components/journey/JourneyLearningTracks.module.css b/src/landings/components/journey/JourneyLearningTracks.module.css
new file mode 100644
index 0000000000..b0dc03bfa5
--- /dev/null
+++ b/src/landings/components/journey/JourneyLearningTracks.module.css
@@ -0,0 +1,213 @@
+.learningTracks {
+ border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ border-radius: 12px;
+ padding: 1.5rem;
+ padding-bottom: .75rem;
+ margin-bottom: 1rem;
+ margin-left: 1rem;
+ box-shadow:
+ 0px 1px 3px 0px rgba(31, 35, 40, 0.08),
+ 0px 1px 0px 0px rgba(31, 35, 40, 0.06);
+ position: relative;
+ z-index: 1;
+ background-color: var(--bgColor-default, var(--color-canvas-default, #ffffff));
+}
+
+.trackHeader {
+ margin: 0 0 0.5rem 0;
+ padding-right: 3rem;
+ color: var(--fgColor-default, var(--color-fg-default, #1f2328));
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.anchorLink {
+ color: var(--fgColor-default, var(--color-fg-default, #1f2328));
+ text-decoration: none;
+}
+
+.trackDescription {
+ margin: 0 0 1rem 0;
+ color: var(--fgColor-muted, var(--color-fg-muted, #656d76));
+}
+
+.expandButton {
+ top: 0;
+ right: 0;
+}
+
+.trackGuides {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ padding-left: 0;
+ list-style: none;
+ counter-reset: guide-counter;
+}
+
+.trackGuides li {
+ margin-bottom: 0.75rem;
+ padding-left: 2.5rem;
+ position: relative;
+ counter-increment: guide-counter;
+}
+
+.trackGuides li::before {
+ content: counter(guide-counter);
+ position: absolute;
+ left: 0;
+ top: -0.125rem;
+ width: 1.5rem;
+ height: 1.5rem;
+ background-color: transparent;
+ border: 1px solid var(--borderColor-default, var(--color-border-default, #d0d7de));
+ color: var(--fgColor-muted, var(--color-fg-muted, #656d76));
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.70rem;
+ font-weight: 600;
+ line-height: 1;
+}
+
+.guideLink {
+ color: var(--fgColor-accent, var(--color-accent-fg, #0969da));
+ text-decoration: none;
+}
+
+/* Hide only the timeline line that extends below the last badge, preserve everything else */
+.timelineContainer :global(.Timeline-Item:last-child::before) {
+ background: linear-gradient(to bottom, var(--borderColor-default, #d1d9e0) 0%, var(--borderColor-default, #d1d9e0) 30%, transparent 30%, transparent 100%) !important;
+}
+
+.timelineBadge {
+ background-color: var(--color-canvas-subtle, #f6f8fa) !important;
+ border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0)) !important;
+}
+
+/* Fix entire timeline component overlapping header */
+.timelineContainer {
+ z-index: 0 !important;
+ position: relative;
+}
+
+.timelineContainer :global(.Timeline) {
+ z-index: 0 !important;
+}
+
+.timelineThinLine :global(.Timeline-Item::before) {
+ width: 1px !important;
+}
+
+/* Mobile-first: custom stacked layout */
+.mobileLayout {
+ display: block;
+ position: relative;
+ z-index: 0;
+}
+
+.mobileItem {
+ margin-bottom: 2rem;
+ text-align: center; /* Only for centering the badge */
+ position: relative;
+}
+
+.mobileBadge {
+ width: 2rem;
+ height: 2rem;
+ background-color: var(--color-canvas-subtle, #f6f8fa);
+ color: var(--fgColor-muted, var(--color-fg-muted));
+ border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ border-radius: 50%;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+ position: relative;
+ z-index: 2;
+}
+
+/* Add connecting line from badge downward */
+.mobileBadge::after {
+ content: '';
+ position: absolute;
+ left: 50%;
+ top: 100%;
+ width: 1px;
+ height: 2.5rem;
+ background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ transform: translateX(-50%);
+ z-index: 1;
+}
+
+/* Add connecting line above badge (except first item) */
+.mobileItem:not(:first-child) .mobileBadge::before {
+ content: '';
+ position: absolute;
+ left: 50%;
+ bottom: 100%;
+ width: 1px;
+ height: 2.5rem;
+ background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ transform: translateX(-50%);
+ z-index: 1;
+}
+
+.mobileConnector {
+ width: 1px;
+ height: 1rem;
+ background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ margin: 0 auto;
+}
+
+.mobileConnector:last-child {
+ display: none; /* Hide connector after last item */
+}
+
+.mobileItem .mobileTile {
+ border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0));
+ border-radius: 12px;
+ padding: 1rem;
+ margin-top: 0.5rem;
+ text-align: left;
+ box-shadow:
+ 0px 1px 3px 0px rgba(31, 35, 40, 0.08),
+ 0px 1px 0px 0px rgba(31, 35, 40, 0.06);
+ position: relative;
+ z-index: 3;
+ background-color: var(--bgColor-default, var(--color-canvas-default, #ffffff));
+}
+
+/* Desktop: show Timeline component */
+@media (min-width: 768px) {
+ .mobileLayout {
+ display: none;
+ }
+
+ .timelineContainer {
+ display: block;
+ }
+}
+
+/* Mobile: hide Timeline component */
+@media (max-width: 767px) {
+ .timelineContainer {
+ display: none;
+ }
+}
+
+/* Mobile: stack h3 and Token vertically when mobile layout is active */
+@media (max-width: 767px) {
+ .trackHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .trackHeader h3 {
+ margin-bottom: .50rem;
+ }
+}
\ No newline at end of file
diff --git a/src/landings/components/journey/JourneyLearningTracks.tsx b/src/landings/components/journey/JourneyLearningTracks.tsx
new file mode 100644
index 0000000000..87397ec49d
--- /dev/null
+++ b/src/landings/components/journey/JourneyLearningTracks.tsx
@@ -0,0 +1,85 @@
+/* filepath: /workspaces/docs-internal/src/landings/components/journey/JourneyLearningTracks.tsx */
+import { ChevronDownIcon, ChevronUpIcon } from '@primer/octicons-react'
+import { Button, Details, Timeline, Token, useDetails } from '@primer/react'
+import type { JourneyLearningTrack } from './JourneyLanding'
+import styles from './JourneyLearningTracks.module.css'
+
+type JourneyLearningTracksProps = {
+ tracks: JourneyLearningTrack[]
+}
+
+export const JourneyLearningTracks = ({ tracks }: JourneyLearningTracksProps) => {
+ if (!tracks || tracks.length === 0) {
+ return null
+ }
+
+ const renderTrackContent = (track: JourneyLearningTrack, trackIndex: number) => {
+ const { getDetailsProps, open } = useDetails({})
+
+ return (
+ <>
+
+
{track.title}
+
+
+ {track.description}
+
+
+
+ {(track.guides || []).map((guide) => (
+ -
+
+ {guide.title}
+
+
+ ))}
+
+
+ >
+ )
+ }
+
+ return (
+ <>
+ {/* Desktop: Timeline component */}
+
+
+ {tracks.map((track, trackIndex) => {
+ return (
+
+ {trackIndex + 1}
+
+ {renderTrackContent(track, trackIndex)}
+
+
+ )
+ })}
+
+
+
+ {/* Mobile: Custom stacked layout */}
+
+ {tracks.map((track, trackIndex) => (
+
+
{trackIndex + 1}
+
+
{renderTrackContent(track, trackIndex)}
+
+ {trackIndex < tracks.length - 1 &&
}
+
+ ))}
+
+ >
+ )
+}