diff --git a/apps/docs/content/index.mdx b/apps/docs/content/index.mdx index 2b1bc726b..06a66e80b 100644 --- a/apps/docs/content/index.mdx +++ b/apps/docs/content/index.mdx @@ -8,12 +8,12 @@ import {LinkExternalIcon} from '@primer/octicons-react' - Primer Brand - Read the installation instructions to get started. + + Read the installation instructions to get started. + + + + Primer + + +
+ +
+ + ) +} diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/header.module.css b/apps/docs/src/@primer/gatsby-theme-doctocat/components/header.module.css new file mode 100644 index 000000000..30f8d2179 --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/header.module.css @@ -0,0 +1,47 @@ +.PageHeader { + position: sticky; + top: 0; + width: 100%; + display: flex; + align-items: center; + padding: var(--base-size-16) var(--base-size-24) var(--base-size-16) + var(--base-size-24); + border-bottom: var(--brand-borderWidth-thin) solid + var(--brand-color-border-muted); + background: var(--brand-color-canvas-default); + z-index: 1; +} + +.SiteTitle { + display: flex; + gap: var(--base-size-8); + align-items: center; + text-decoration: none; +} + +.SiteTitle svg { + fill: var(--brand-color-text-default); +} + +.Title { + display: inline-block; + font-weight: var(--base-text-weight-semibold); + color: var(--brand-color-text-default); + font-family: var(--brand-fontStack-sansSerif); + padding-inline-end: var(--base-size-12); +} + +.SidePanel { + display: none; + margin-left: auto; +} + +@media (max-width: 1012px) { + .PageHeader { + padding: var(--base-size-16); + } + + .SidePanel { + display: block; + } +} diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.js b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.js new file mode 100644 index 000000000..79a4e5f54 --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.js @@ -0,0 +1,41 @@ +import React from 'react' +import {Head, Header, Hero, Sidebar} from '@primer/gatsby-theme-doctocat' +import PageFooter from '@primer/gatsby-theme-doctocat/src/components/page-footer' +import {Stack} from '@primer/react-brand' +import styles from './hero-layout.module.css' + +function HeroLayout({children, pageContext}) { + let {additionalContributors} = pageContext.frontmatter + + if (!additionalContributors) { + additionalContributors = [] + } + + return ( +
+ +
+
+
+ +
+
+
+ + + {children} + ({login})), + )} + /> + +
+
+
+
+ ) +} + +export default HeroLayout diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.module.css b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.module.css new file mode 100644 index 000000000..802242c19 --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero-layout.module.css @@ -0,0 +1,38 @@ +.HeroPageWrapper { + flex-direction: column; + min-height: 100vh; + display: flex; +} + +.HeroLayoutWrapper { + display: flex; + flex: 1 1 auto; + flex-direction: row; +} + +.HeroLayoutSidebarWrapper { + display: block; + + @media screen and (max-width: 64rem) { + display: none; + } +} + +.HeroLayoutMain { + padding: var(--base-size-24); + width: 100%; + @media screen and (max-width: 48rem) { + padding-bottom: 0; + } +} + +.LandingPageLayout { + max-width: var(--breakpoint-xlarge, 80rem); + margin: 0 auto; + width: 100%; + padding: 0 var(--base-size-48); + + @media screen and (max-width: 64rem) { + padding: 0; + } +} diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.js b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.js index b74e23b8b..c690d75bf 100644 --- a/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.js @@ -1,38 +1,31 @@ -import {Box as PRCBox, Heading, Text, ThemeProvider} from '@primer/react' import React from 'react' -import {Container} from '@primer/gatsby-theme-doctocat' -import heroIllustration from '../primer-components-hero.svg' -import {version} from '../../../../../../packages/react/package' +import {Heading, Stack, Text} from '@primer/react-brand' +import styles from './hero.module.css' export default function Hero() { return ( - - - - - Primer Brand - - - v{version} - - - - - + + + + Primer Brand UI + + + Primer Brand is GitHub's design system for creating marketing websites and digital experiences. + + +
+ 3d illustration, showing a side profile of Mona the GitHub Mascot gazing towards the sky +
+
) } diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.module.css b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.module.css new file mode 100644 index 000000000..23688f634 --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/hero.module.css @@ -0,0 +1,6 @@ +.HeroImage { + flex-shrink: 0; + @media screen and (max-width: 64rem) { + display: none; + } +} diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js b/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js new file mode 100644 index 000000000..ea4f81f1a --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.js @@ -0,0 +1,60 @@ +import {NavList} from '@primer/react/drafts' +import {useLocation} from '@reach/router' +import {Link as GatsbyLink, withPrefix} from 'gatsby' +import React from 'react' +import VisuallyHidden from '@primer/gatsby-theme-doctocat/src/components/visually-hidden' +import styles from './nav-items.module.css' + +function NavItem({href, children}) { + const location = useLocation() + const isCurrent = withPrefix(href) === location.pathname + return ( + + {children} + + ) +} + +function NavItems({items}) { + return ( + <> + +

Site navigation

+
+
+ + {items.map((item) => ( + + {item.children ? ( + + {item.children.map((child) => ( + + {child.title} + {child.children ? ( + + {child.children.map((subChild) => ( + + {subChild.title} + + ))} + + ) : null} + + ))} + + ) : ( + {item.title} + )} + + ))} + +
+ + ) +} + +export default NavItems diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.module.css b/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.module.css new file mode 100644 index 000000000..595f4336a --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/nav-items.module.css @@ -0,0 +1,11 @@ +/* NavList style overrides needed because NavList uses hard-coded pixel values instead of rem-based CSS variables */ +.NavList__Container li span { + font-size: var(--brand-text-size-100); +} + +.NavList__Container li a span, +.NavList__Container li button span { + font-size: var(--brand-text-size-200); + line-height: var(--brand-text-lineHeight-200); +} +/* END NavList overrides */ diff --git a/apps/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js b/apps/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js new file mode 100644 index 000000000..2ed8ffb0f --- /dev/null +++ b/apps/docs/src/@primer/gatsby-theme-doctocat/components/sidebar.js @@ -0,0 +1,121 @@ +import {Box, TextInput} from '@primer/react' +import {Text, Stack} from '@primer/react-brand' +import {SearchIcon} from '@primer/octicons-react' +import React, {useState} from 'react' +import navItems from '../nav.yml' +import {HEADER_HEIGHT} from './header' +import NavItems from './nav-items' + +function usePersistentScroll(id) { + const ref = React.useRef() + + const handleScroll = React.useCallback( + // Save scroll position in session storage on every scroll change + (event) => window.sessionStorage.setItem(id, event.target.scrollTop), + [id], + ) + + React.useLayoutEffect(() => { + // Restore scroll position when component mounts + const scrollPosition = window.sessionStorage.getItem(id) + if (scrollPosition && ref.current) { + ref.current.scrollTop = scrollPosition + } + }, [id]) + + // Return props to spread onto the scroll container + return { + ref, + onScroll: handleScroll, + } +} + +function Sidebar({inSideSheetDialog}) { + const scrollContainerProps = usePersistentScroll('sidebar') + const [filter, setFilter] = useState('') + + const handleFilterChange = (e) => { + setFilter(e.target.value.toLowerCase()) + } + + const filterNavItems = (item) => { + if (item.title.toLowerCase().includes(filter)) { + return true + } + if (item.children) { + // Filter children that match the filter + const filteredChildren = item.children.filter((child) => + child.title.toLowerCase().includes(filter), + ) + // If there are any matching children, return a new item with filtered children + if (filteredChildren.length > 0) { + return { + ...item, + children: filteredChildren, + } + } + } + return false + } + + const filteredNavItems = navItems + .map((item) => { + if (filterNavItems(item)) { + return item.children + ? {...item, children: item.children.filter(filterNavItems)} + : item + } + return null + }) + .filter(Boolean) + + return ( + + + + + Brand UI + +
+ + + + +
+
+
+
+ ) +} + +export default Sidebar diff --git a/apps/docs/src/components/navigation/navigation.module.css b/apps/docs/src/components/navigation/navigation.module.css new file mode 100644 index 000000000..6e611fd2f --- /dev/null +++ b/apps/docs/src/components/navigation/navigation.module.css @@ -0,0 +1,80 @@ +.Navigation { + display: flex; + list-style: none; + align-items: center; + flex-grow: 1; + font-family: var(--brand-fontStack-sansSerif); +} + +.Separator { + color: var(--borderColor-default, #d0d7de); + font-weight: var(--base-text-weight-light); +} + +.HorizontalList { + display: flex; + list-style: none; + align-items: center; + flex-grow: 1; + margin: 0; + padding: 0; + gap: var(--base-size-2); + padding-right: var(--base-size-16); + justify-content: flex-end; +} + +.Link { + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--brand-color-text-muted); + text-decoration: none; + padding: 0 var(--base-size-12); + height: var(--base-size-32); + border: 1px solid transparent; + border-radius: var(--brand-borderRadius-medium); +} + +.DropdownLink { + color: var(--brand-color-text-muted); + font-size: var(--brand-text-size-200); +} + +.Dropdown svg { + color: var(--brand-color-text-default); +} + +.Icon { + margin-bottom: 6px; +} + +.Link:hover { + background: var( + --bgColor-muted, + #f6f8fa + ); /* Note: this color will be wrong when/if we add dark mode Doctocat Primer Brand docs */ +} + +.Link:focus-visible { + outline: 2px solid var(--focus-outlineColor); + outline-offset: -2px; +} + +.Link[data-active] { + color: var(--brand-color-text-default); + font-weight: var(--base-text-weight-semibold); +} + +.HorizontalList { + display: none; +} + +@media (min-width: 1012px) { + .HorizontalList { + display: flex; + } + .Separator, + .Dropdown { + display: none; + } +} diff --git a/apps/docs/src/components/navigation/navigation.tsx b/apps/docs/src/components/navigation/navigation.tsx new file mode 100644 index 000000000..dce6b49f8 --- /dev/null +++ b/apps/docs/src/components/navigation/navigation.tsx @@ -0,0 +1,86 @@ +import React from 'react' +import {ArrowUpRightIcon, TriangleDownIcon} from '@primer/octicons-react' +import styles from './navigation.module.css' +import {ActionList, ActionMenu, Button} from '@primer/react' + +const navItems = [ + {href: 'https://primer.style/', label: 'Product UI'}, // TODO: update to https://primer.style/product when we launch the new docs site + {href: '/', label: 'Brand UI'}, + {href: 'https://primer.style/octicons', label: 'Octicons'}, + {href: 'https://primer.style/guides/accessibility', label: 'Accessibility'}, // TODO: update to https://primer.style/accessibility when we launch the new docs site + {href: 'https://brand.github.com/', label: 'Brand Toolkit', external: true}, +] + +export function Navigation() { + const activeItem = navItems.find((item) => item.label === 'Brand UI') + return ( + + ) +} diff --git a/apps/docs/src/components/side-panel/side-panel.module.css b/apps/docs/src/components/side-panel/side-panel.module.css new file mode 100644 index 000000000..907d6225f --- /dev/null +++ b/apps/docs/src/components/side-panel/side-panel.module.css @@ -0,0 +1,10 @@ +.SidePanel { + position: relative; +} + +.Header { + padding: var(--base-size-16); + border-bottom: var(--brand-borderWidth-thin) solid var(--brand-color-border-muted); + display: flex; + justify-content: end; +} diff --git a/apps/docs/src/components/side-panel/side-panel.tsx b/apps/docs/src/components/side-panel/side-panel.tsx new file mode 100644 index 000000000..bddcd3b6e --- /dev/null +++ b/apps/docs/src/components/side-panel/side-panel.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import {useState, useRef} from 'react' +import {IconButton} from '@primer/react' +import {Dialog} from '@primer/react/experimental' +import {ThreeBarsIcon, XIcon} from '@primer/octicons-react' +import styles from './side-panel.module.css' +import {Sidebar} from '@primer/gatsby-theme-doctocat' + +export function SidePanel() { + const [isOpen, setIsOpen] = useState(false) + const closeButtonRef = useRef(null) + const openButtonRef = useRef(null) + + const openDialog = () => { + setIsOpen(true) + } + + const closeDialog = () => { + setIsOpen(false) + } + + const renderHeader = ({dialogLabelId}: {dialogLabelId: string}) => ( +
+ +
+ ) + + return ( +
+ + {isOpen && ( + + + + )} +
+ ) +}