diff --git a/apps/website/pages/components/quick-nav/code.tsx b/apps/website/pages/components/quick-nav/code.tsx new file mode 100644 index 0000000000..9cd9775592 --- /dev/null +++ b/apps/website/pages/components/quick-nav/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import QuickNavCodePage from "screens/components/quick-nav/code/QuickNavCodePage"; +import QuickNavPageLayout from "screens/components/quick-nav/QuickNavPageLayout"; + +const Code = () => ( + <> + + Quick Nav Code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/quick-nav/index.tsx b/apps/website/pages/components/quick-nav/index.tsx index d370136998..292a2d9043 100644 --- a/apps/website/pages/components/quick-nav/index.tsx +++ b/apps/website/pages/components/quick-nav/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; import QuickNavPageLayout from "screens/components/quick-nav/QuickNavPageLayout"; -import QuickNavCodePage from "screens/components/quick-nav/code/QuickNavCodePage"; +import QuickNavOverviewPage from "screens/components/quick-nav/overview/QuickNavOverviewsPage"; -const Index = () => { - return ( - <> - - Quick Nav — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Quick Nav — Halstack Design System + + + +); -Index.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; export default Index; diff --git a/apps/website/pages/components/quick-nav/specifications.tsx b/apps/website/pages/components/quick-nav/specifications.tsx deleted file mode 100644 index 37af9ea499..0000000000 --- a/apps/website/pages/components/quick-nav/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import QuickNavPageLayout from "screens/components/quick-nav/QuickNavPageLayout"; -import QuickNavSpecsPage from "screens/components/quick-nav/specs/QuickNavSpecsPage"; - -const Specifications = () => { - return ( - <> - - Quick Nav Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/quick-nav/usage.tsx b/apps/website/pages/components/quick-nav/usage.tsx deleted file mode 100644 index 4695bd383d..0000000000 --- a/apps/website/pages/components/quick-nav/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import QuickNavPageLayout from "screens/components/quick-nav/QuickNavPageLayout"; -import QuickNavUsagePage from "screens/components/quick-nav/usage/QuickNavUsagePage"; - -const Usage = () => { - return ( - <> - - Quick Nav Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx b/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx index 78ccea72d3..e38de4aedb 100644 --- a/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx +++ b/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx @@ -2,13 +2,13 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import Code from "@/common/Code"; import { ReactNode } from "react"; const QuickNavPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/quick-nav" }, - { label: "Usage", path: "/components/quick-nav/usage" }, - { label: "Specifications", path: "/components/quick-nav/specifications" }, + { label: "Overview", path: "/components/quick-nav" }, + { label: "Code", path: "/components/quick-nav/code" }, ]; return ( @@ -17,11 +17,12 @@ const QuickNavPageHeading = ({ children }: { children: ReactNode }) => { - The quick nav component allows navigation inside a page. It renders the links according to the headings of - the content in order to navigate to each section. The navigation is done using the link label or the link - label plus sublink label when it is a sublink. If there is any space, it will be replaced by '-'. + The Quick Nav component allows navigation inside a page. It renders links according to the headings of the + content structure, enabling users to jump to specific sections. The navigation label is based on the section + title or a combination of section and sub-section titles (for nested links). If a heading includes spaces, + they are replaced with hyphens (-) in the URL. - + {children} diff --git a/apps/website/screens/components/quick-nav/code/QuickNavCodePage.tsx b/apps/website/screens/components/quick-nav/code/QuickNavCodePage.tsx index a9ba99a767..dd28e53a03 100644 --- a/apps/website/screens/components/quick-nav/code/QuickNavCodePage.tsx +++ b/apps/website/screens/components/quick-nav/code/QuickNavCodePage.tsx @@ -22,14 +22,6 @@ const sections = [ - - title - - string - - Title of the quick nav component. - - - @@ -56,6 +48,14 @@ const sections = [ - + + title + + string + + Title of the quick nav component. + - + ), @@ -83,15 +83,13 @@ const sections = [ }, ]; -const QuickNavCodePage = () => { - return ( - - - - - - - ); -}; +const QuickNavCodePage = () => ( + + + + + + +); export default QuickNavCodePage; diff --git a/apps/website/screens/components/quick-nav/overview/QuickNavOverviewsPage.tsx b/apps/website/screens/components/quick-nav/overview/QuickNavOverviewsPage.tsx new file mode 100644 index 0000000000..203785b470 --- /dev/null +++ b/apps/website/screens/components/quick-nav/overview/QuickNavOverviewsPage.tsx @@ -0,0 +1,87 @@ +import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; +import Image from "@/common/Image"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import anatomyImage from "./images/quickNav_anatomy.png"; + +const sections = [ + { + title: "Introduction", + content: ( + + Quick Nav is used to improve in-page navigation by listing content sections and subsections based on the heading + hierarchy. This helps users understand the page structure at a glance and jump directly to the content they're + interested in. It's especially useful on documentation pages, dashboards and long-form content. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Quick nav anatomy + + + Divider: a thin vertical line that visually separates the Quick Nav from the main content + area. Its purpose is to create a clear boundary between navigation and content, improving readability and + layout organization. + + + Title (Optional): a short descriptive title such as "Contents" or "On this page" + that provides context for the navigation list. + + + Links: the main navigation items, each representing a primary section on the page. These + are generated from top-level headings and are clickable. + + + Sublinks (Optional): secondary navigation items, generated from subheadings nested + under the main sections. These allow finer-grain navigation within a specific topic. + + + + ), + }, + { + title: "Best practices", + content: ( + + + Provide a clear title: use a meaningful title like "On this page" to help users understand + the context of the links. + + + Show structure clearly: use Quick Nav to mirror the hierarchy of your content, making it + easier to follow and navigate. Keep headings descriptive and consistent so link labels are meaningful when + rendered. + + + Avoid clutter: avoid rendering Quick Nav on pages with very little content; it can feel + redundant. + + + Maintain visual separation from main content: always allow sufficient spacing between the + Quick Nav and the main content area. This helps users visually distinguish navigation from content and avoids + overwhelming the layout. Use padding or margins to ensure the Quick Nav doesn't feel cramped or interfere with + readability, especially on larger screens or dense layouts. + + + Pair with other navigational tools: combine Quick Nav with tabs or breadcrumbs for a + comprehensive navigation experience across and within pages. + + + ), + }, +]; + +const QuickNavOverviewPage = () => ( + + + + + + +); + +export default QuickNavOverviewPage; diff --git a/apps/website/screens/components/quick-nav/overview/images/quickNav_anatomy.png b/apps/website/screens/components/quick-nav/overview/images/quickNav_anatomy.png new file mode 100644 index 0000000000..c4fff8511a Binary files /dev/null and b/apps/website/screens/components/quick-nav/overview/images/quickNav_anatomy.png differ diff --git a/apps/website/screens/components/quick-nav/specs/QuickNavSpecsPage.tsx b/apps/website/screens/components/quick-nav/specs/QuickNavSpecsPage.tsx deleted file mode 100644 index ce2dce75e9..0000000000 --- a/apps/website/screens/components/quick-nav/specs/QuickNavSpecsPage.tsx +++ /dev/null @@ -1,273 +0,0 @@ -import { DxcTable, DxcBulletedList, DxcFlex } from "@dxc-technology/halstack-react"; -import Image from "@/common/Image"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import Figure from "@/common/Figure"; -import Code from "@/common/Code"; -import DocFooter from "@/common/DocFooter"; -import specsImage from "./images/quickNav_specs.png"; -import anatomyImage from "./images/quickNav_anatomy.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Quicknav design specifications -
- ), - }, - { - title: "Anatomy", - content: ( - <> - Quick nav anatomy - - - Title (Optional) - - Links - - Sublinks (Optional) - - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - fontColor - - Link - - color-grey-700 - - #666666 - - - - hoverFontColor - - Link - - color-purple-600 - - #7d2fd0 - - - - dividerBorderColor - - Divider - - color-grey-400 - - #bfbfbf - - - - ), - }, - { - title: "Typography", - content: ( - - - - Property - Element - Core token - Value - - - - - - fontFamily - - Link - - font-family-sans - - Open Sans, sans-serif - - - - fontSize - - Link - - font-scale-02 - - 0.875rem - - - - fontStyle - - Link - - font-normal - - normal - - - - fontWeight - - Link - - font-regular - - 400 - - - - ), - }, - { - title: "Spacing", - content: ( - - - - Property - Element - Core token - Value - - - - - - paddingTop - - Content - - spacing-3 - - 0.5rem / 8px - - - - paddingBottom - - Content - - spacing-3 - - 0.5rem / 8px - - - - paddingLeft - - Content - - spacing-5 - - 1rem / 16px - - - - paddingRight - - Content - - spacing-5 - - 1rem / 16px - - - - ), - }, - { - title: "Border", - content: ( - - - - Property - Element - Core token - Value - - - - - - focusBorderColor - - Link - - color-blue-600 - - #0095ff - - - - focusBorderStyle - - Link - - border-style-solid - - solid - - - - focusBorderThickness - - Link - - border-radius-small - - 0.125rem / 2px - - - - focusBorderRadius - - Link - - border-radius-small - - 0.125rem / 2px - - - - ), - }, - ], - }, -]; - -const QuickNavSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default QuickNavSpecsPage; diff --git a/apps/website/screens/components/quick-nav/specs/images/quickNav_anatomy.png b/apps/website/screens/components/quick-nav/specs/images/quickNav_anatomy.png deleted file mode 100644 index 36c280634c..0000000000 Binary files a/apps/website/screens/components/quick-nav/specs/images/quickNav_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/quick-nav/specs/images/quickNav_specs.png b/apps/website/screens/components/quick-nav/specs/images/quickNav_specs.png deleted file mode 100644 index de9fe101ef..0000000000 Binary files a/apps/website/screens/components/quick-nav/specs/images/quickNav_specs.png and /dev/null differ diff --git a/apps/website/screens/components/quick-nav/usage/QuickNavUsagePage.tsx b/apps/website/screens/components/quick-nav/usage/QuickNavUsagePage.tsx deleted file mode 100644 index b3cd152c30..0000000000 --- a/apps/website/screens/components/quick-nav/usage/QuickNavUsagePage.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { DxcParagraph, DxcBulletedList, DxcFlex } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; - -const sections = [ - { - title: "Usage", - content: ( - <> - - The quick nav displays the content of a page or section by order of hierarchy and headings. - - - - Use quick nav to show users an overview of a page or section's content and allow them to navigate - directly to it. - - - Quick nav is particularly useful for pages with long sections of content. - - - Quick nav works well with tabs when organizing and presenting easily navigable content in terms of main and - sub-sections. - - - - ), - }, -]; - -const QuickNavUsagePage = () => { - return ( - - - - - - - ); -}; - -export default QuickNavUsagePage; diff --git a/packages/lib/src/quick-nav/QuickNav.stories.tsx b/packages/lib/src/quick-nav/QuickNav.stories.tsx index 9f52a423b2..ae99963bd3 100644 --- a/packages/lib/src/quick-nav/QuickNav.stories.tsx +++ b/packages/lib/src/quick-nav/QuickNav.stories.tsx @@ -1,7 +1,6 @@ import styled from "styled-components"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; -import { HalstackProvider } from "../HalstackContext"; import DxcHeading from "../heading/Heading"; import DxcParagraph from "../paragraph/Paragraph"; import DxcQuickNav from "./QuickNav"; @@ -12,13 +11,6 @@ export default { component: DxcQuickNav, } as Meta; -const opinionatedTheme = { - quickNav: { - fontColor: "#666666", - accentColor: "#9a6bb2", - }, -}; - const defaultLinks = [ { label: "Overview", @@ -346,12 +338,6 @@ const QuickNav = () => ( - - <ExampleContainer> - <HalstackProvider theme={opinionatedTheme}> - <DxcQuickNav links={defaultLinks} /> - </HalstackProvider> - </ExampleContainer> </> ); diff --git a/packages/lib/src/quick-nav/QuickNav.tsx b/packages/lib/src/quick-nav/QuickNav.tsx index dcb1666c53..e5ab3dbf4e 100644 --- a/packages/lib/src/quick-nav/QuickNav.tsx +++ b/packages/lib/src/quick-nav/QuickNav.tsx @@ -1,104 +1,90 @@ import { useContext } from "react"; import slugify from "slugify"; -import styled, { ThemeProvider } from "styled-components"; -import DxcFlex from "../flex/Flex"; +import styled from "styled-components"; import DxcHeading from "../heading/Heading"; -import DxcInset from "../inset/Inset"; import DxcTypography from "../typography/Typography"; -import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; +import { HalstackLanguageContext } from "../HalstackContext"; import QuickNavTypes from "./types"; const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { const translatedLabels = useContext(HalstackLanguageContext); - const colorsTheme = useContext(HalstackContext); - return ( - <ThemeProvider theme={colorsTheme.quickNav}> - <QuickNavContainer> - <DxcFlex direction="column" gap="var(--spacing-gap-s)"> - <DxcHeading level={4} text={title || translatedLabels.quickNav.contentTitle} /> - <ListColumn> - {links.map((link) => ( - <li key={link.label}> - <DxcInset space="var(--spacing-padding-xxs)"> - <DxcTypography> - <Link href={`#${slugify(link.label, { lower: true })}`}>{link.label}</Link> - <ListSecondColumn> - {link.links?.map((sublink) => ( - <li key={sublink.label}> - <DxcInset horizontal="var(--spacing-padding-xs)"> - <DxcTypography> - <Link - href={`#${slugify(link?.label, { lower: true })}-${slugify(sublink?.label, { - lower: true, - })}`} - > - {sublink.label} - </Link> - </DxcTypography> - </DxcInset> - </li> - ))} - </ListSecondColumn> - </DxcTypography> - </DxcInset> - </li> - ))} - </ListColumn> - </DxcFlex> - </QuickNavContainer> - </ThemeProvider> - ); -}; - -const QuickNavContainer = styled.div` - padding-top: ${(props) => props.theme.paddingTop}; - padding-bottom: ${(props) => props.theme.paddingBottom}; - padding-left: ${(props) => props.theme.paddingLeft}; - padding-right: ${(props) => props.theme.paddingRight}; - border-left: 2px solid ${(props) => props.theme.dividerBorderColor}; -`; + const QuickNavContainer = styled.div` + display: flex; + flex-direction: column; + gap: var(--spacing-gap-m); + padding: var(--spacing-padding-xs) var(--spacing-padding-m); + border-left: var(--border-width-m) var(--border-style-default) var(--border-color-neutral-medium); + `; -const ListColumn = styled.ul` - display: flex; - flex-direction: column; - gap: 0.5rem; - margin: 0; - padding: 0; - list-style-type: none; -`; + const ListColumn = styled.ul` + display: flex; + flex-direction: column; + gap: var(--spacing-gap-s); + margin: 0; + padding: 0; + list-style-type: none; + `; -const ListSecondColumn = styled.ul` - display: flex; - flex-direction: column; - margin: 0; - padding: 0; - list-style-type: none; -`; + const ListSecondColumn = styled.ul` + display: flex; + flex-direction: column; + gap: var(--spacing-gap-xs); + margin-top: var(--spacing-gap-xs); + padding: var(--spacing-padding-none) var(--spacing-padding-xs); + list-style-type: none; + `; -const Link = styled.a` - text-decoration: none; - font-size: ${(props) => props.theme.fontSize}; - font-family: ${(props) => props.theme.fontFamily}; - font-style: ${(props) => props.theme.fontStyle}; - font-weight: ${(props) => props.theme.fontWeight}; - color: ${(props) => props.theme.fontColor}; - display: block; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - width: fit-content; - max-width: 100%; + const Link = styled.a` + text-decoration: none; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-regular); + color: var(--color-fg-neutral-stronger); + display: block; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: fit-content; + max-width: 100%; - &:hover { - color: ${(props) => props.theme.hoverFontColor}; - } - &:focus { - outline-color: ${(props) => props.theme.focusBorderColor}; - outline-style: ${(props) => props.theme.focusBorderStyle}; - outline-width: ${(props) => props.theme.focusBorderThickness}; - border-radius: ${(props) => props.theme.focusBorderRadius}; - } -`; + &:hover { + color: var(--color-fg-primary-strong); + } + &:focus { + border-radius: var(--border-radius-xs); + border: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + } + `; + return ( + <QuickNavContainer> + <DxcHeading level={4} text={title || translatedLabels.quickNav.contentTitle} /> + <ListColumn> + {links.map((link) => ( + <li key={link.label}> + <DxcTypography> + <Link href={`#${slugify(link.label, { lower: true })}`}>{link.label}</Link> + <ListSecondColumn> + {link.links?.map((sublink) => ( + <li key={sublink.label}> + <DxcTypography> + <Link + href={`#${slugify(link?.label, { lower: true })}-${slugify(sublink?.label, { + lower: true, + })}`} + > + {sublink.label} + </Link> + </DxcTypography> + </li> + ))} + </ListSecondColumn> + </DxcTypography> + </li> + ))} + </ListColumn> + </QuickNavContainer> + ); +}; export default DxcQuickNav;