diff --git a/apps/website/pages/components/nav-tabs/code.tsx b/apps/website/pages/components/nav-tabs/code.tsx new file mode 100644 index 0000000000..b6e2985afa --- /dev/null +++ b/apps/website/pages/components/nav-tabs/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import NavTabsPageLayout from "screens/components/nav-tabs/NavTabsPageLayout"; +import NavTabsCodePage from "screens/components/nav-tabs/code/NavTabsCodePage"; + +const Code = () => ( + <> + + Nav Tabs Code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/nav-tabs/index.tsx b/apps/website/pages/components/nav-tabs/index.tsx index 765def99ab..d7d3c23176 100644 --- a/apps/website/pages/components/nav-tabs/index.tsx +++ b/apps/website/pages/components/nav-tabs/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; import NavTabsPageLayout from "screens/components/nav-tabs/NavTabsPageLayout"; -import NavTabsCodePage from "screens/components/nav-tabs/code/NavTabsCodePage"; +import NavTabsOverviewPage from "screens/components/nav-tabs/overview/NavTabsOverviewPage"; -const Index = () => { - return ( - <> - - Nav Tabs — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Nav Tabs — 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/nav-tabs/specifications.tsx b/apps/website/pages/components/nav-tabs/specifications.tsx deleted file mode 100644 index 53005dfec3..0000000000 --- a/apps/website/pages/components/nav-tabs/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import NavTabsPageLayout from "screens/components/nav-tabs/NavTabsPageLayout"; -import NavTabsSpecsPage from "screens/components/nav-tabs/specs/NavTabsSpecsPage"; - -const Specifications = () => { - return ( - <> - - Nav Tabs Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/nav-tabs/usage.tsx b/apps/website/pages/components/nav-tabs/usage.tsx deleted file mode 100644 index ae598a1dea..0000000000 --- a/apps/website/pages/components/nav-tabs/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import NavTabsPageLayout from "screens/components/nav-tabs/NavTabsPageLayout"; -import NavTabsUsagePage from "screens/components/nav-tabs/usage/NavTabsUsagePage"; - -const Usage = () => { - return ( - <> - - Nav Tabs Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx b/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx index 4c2a936fdd..16077c5cd3 100644 --- a/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx +++ b/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const NumberInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/nav-tabs" }, - { label: "Usage", path: "/components/nav-tabs/usage" }, - { label: "Specifications", path: "/components/nav-tabs/specifications" }, + { label: "Overview", path: "/components/nav-tabs" }, + { label: "Code", path: "/components/nav-tabs/code" }, ]; return ( @@ -17,8 +16,8 @@ const NumberInputPageHeading = ({ children }: { children: ReactNode }) => { - Nav tabs function in the same way as the tab component but are more focused on navigation across pages or - links. + Nav tabs lets users switch between different views or sections within the same page, organizing related + content into a clear and accessible layout. diff --git a/apps/website/screens/components/nav-tabs/code/NavTabsCodePage.tsx b/apps/website/screens/components/nav-tabs/code/NavTabsCodePage.tsx index 0d5be9e1c8..20157e8204 100644 --- a/apps/website/screens/components/nav-tabs/code/NavTabsCodePage.tsx +++ b/apps/website/screens/components/nav-tabs/code/NavTabsCodePage.tsx @@ -26,16 +26,6 @@ const sections = [ - - iconPosition - - 'top' | 'left' - - Whether the icon should appear above or to the left of the label. - - 'top' - - @@ -51,6 +41,16 @@ const sections = [ - + + iconPosition + + 'top' | 'left' + + Whether the icon should appear above or to the left of the label. + + 'top' + + tabIndex @@ -94,6 +94,19 @@ const sections = [ false + + + + + children + + + + string + + Tab label text. + - + disabled @@ -141,19 +154,6 @@ const sections = [ false - - - - - children - - - - string - - Tab label text. - - - ), diff --git a/apps/website/screens/components/nav-tabs/overview/NavTabsOverviewPage.tsx b/apps/website/screens/components/nav-tabs/overview/NavTabsOverviewPage.tsx new file mode 100644 index 0000000000..768921134f --- /dev/null +++ b/apps/website/screens/components/nav-tabs/overview/NavTabsOverviewPage.tsx @@ -0,0 +1,198 @@ +import { DxcBulletedList, DxcFlex, DxcParagraph, DxcLink, DxcHeading } from "@dxc-technology/halstack-react"; +import DocFooter from "@/common/DocFooter"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import Image from "@/common/Image"; +import navTabsAnatomy from "./images/navTabs_anatomy.png"; +import Link from "next/link"; +import Example from "@/common/example/Example"; +import icons_top from "./examples/icons_top"; +import icons_left from "./examples/icons_left"; +import badges from "./examples/badges"; + +const sections = [ + { + title: "Introduction", + content: ( + + Our nav tabs provide a simple and efficient way to organize and navigate between multiple content sections + within the same page. By dividing related information into distinct tabs, it helps reduce visual clutter and + improves content discoverability without requiring users to leave the current view. This component is especially + useful in scenarios where users need to compare or switch between data sets, settings or categories. It supports + both horizontal and stacked layouts, ensuring adaptability across different use cases and screen sizes. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Nav tabs anatomy + + + Container: the outer wrapper that holds and organizes all tab items. It defines the overall + structure and layout of the navigation tabs, ensuring proper alignment and spacing across the component. + + + Label: the text displayed within each tab that indicates the section or category it leads + to. + + + Notification badge + (Optional): a visual indicator that displays the number of pending actions, alerts or updates + related to a specific tab. + + + Selected tab: the active tab currently in focus, representing the visible content section. + It is visually distinguished from unselected tabs using different color, weight or indicator styling. + + + Icon + (Optional): an optional graphical element placed before the label to visually reinforce the tab's + meaning or category. + + + Selected tab indicator: a horizontal bar that visually marks the currently active tab. + + + Unselected tab indicator: a horizontal bar that visually marks the currently inactive tab. + + + + ), + }, + { + title: "Using nav tabs", + subSections: [ + { + title: "Icon position", + content: ( + <> + + Icons in nav tabs can be placed either above or to the left of the label, depending on + the layout and density of the interface. + + + + Use the top position when the tabs are displayed in a horizontal layout and you want to + emphasize the icon as a key visual cue—ideal for dashboards or mobile - first interfaces where vertical + stacking feels more natural. + + + + Place the icon to the left of the label in more compact environments, especially when + space is limited. This configuration maintains readability while preserving a clean, organized + structure. + + + + + Choose the position that best aligns with the visual hierarchy and user flow of your application. + + + ), + }, + { + title: "Notification badges", + content: ( + <> + + Nav tabs can optionally include a notification badge to indicate new activity, alerts, or + content updates related to that tab. These badges are typically displayed as small counters and serve as a + visual prompt to draw the user's attention to something that requires their interaction + or awareness. Use notification badges sparingly and only when there is a clear need to highlight important + changes—such as unread messages, pending actions or system alerts—to avoid visual noise and ensure they + retain their impact. + + + + ), + }, + { + title: "Nav tabs vs. Tabs", + content: ( + <> + + Both{" "} + + tabs + {" "} + and nav tabs are used for navigation, but they serve different purposes and function in distinct ways. + + + + + Act as primary navigation elements, often leading to different pages or sections of an + application. + + + Clicking on a nav tab may trigger a full page reload or route change. + + + + + + Used to switch between different content sections within the same page or container. + + + Typically do not trigger a full page reload but update content dynamically. + + + + Both components improve usability, but tabs are best for grouping related content within + a page, while + nav tabs help users move across different sections or pages of an application. + + + ), + }, + ], + }, + { + title: "Best practices", + content: ( + <> + + + Use nav tabs to group related content or views under a shared context, making it easier for + users to switch between them without losing orientation. + + + Keep nav tab items labels short and descriptive to avoid truncation and maintain clarity, + especially on smaller viewports. + + + Ensure that tabs follow a logical order — based on frequency of use, workflow, or user + priority. + + + Use notification badges to highlight relevant updates only when necessary, and avoid + overloading multiple tabs with badges at once. + + + Choose icon placement (left or top) based on the available space and the importance of the icon in the + context of the label. Left is preferred for horizontal layouts; top works best in vertical or space - + constrained scenarios. + + + Avoid mixing navigation tabs and action buttons within the same group, as this can create confusion around + expected behavior. + + + + ), + }, +]; + +const NavTabsOverviewPage = () => { + return ( + + + + + + + ); +}; + +export default NavTabsOverviewPage; diff --git a/apps/website/screens/components/nav-tabs/overview/examples/badges.ts b/apps/website/screens/components/nav-tabs/overview/examples/badges.ts new file mode 100644 index 0000000000..79a2fac169 --- /dev/null +++ b/apps/website/screens/components/nav-tabs/overview/examples/badges.ts @@ -0,0 +1,26 @@ +import { DxcNavTabs, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + return ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + + ); +}`; + +const scope = { + DxcNavTabs, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/nav-tabs/overview/examples/icons_left.ts b/apps/website/screens/components/nav-tabs/overview/examples/icons_left.ts new file mode 100644 index 0000000000..77c3437f37 --- /dev/null +++ b/apps/website/screens/components/nav-tabs/overview/examples/icons_left.ts @@ -0,0 +1,26 @@ +import { DxcNavTabs, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + return ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + + ); +}`; + +const scope = { + DxcNavTabs, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/nav-tabs/overview/examples/icons_top.ts b/apps/website/screens/components/nav-tabs/overview/examples/icons_top.ts new file mode 100644 index 0000000000..04296b8042 --- /dev/null +++ b/apps/website/screens/components/nav-tabs/overview/examples/icons_top.ts @@ -0,0 +1,26 @@ +import { DxcNavTabs, DxcInset } from "@dxc-technology/halstack-react"; + +const code = `() => { + return ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + + ); +}`; + +const scope = { + DxcNavTabs, + DxcInset, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/nav-tabs/overview/images/navTabs_anatomy.png b/apps/website/screens/components/nav-tabs/overview/images/navTabs_anatomy.png new file mode 100644 index 0000000000..c3514b5964 Binary files /dev/null and b/apps/website/screens/components/nav-tabs/overview/images/navTabs_anatomy.png differ diff --git a/apps/website/screens/components/nav-tabs/specs/images/navTabs_specs.png b/apps/website/screens/components/nav-tabs/overview/images/navTabs_specs.png similarity index 100% rename from apps/website/screens/components/nav-tabs/specs/images/navTabs_specs.png rename to apps/website/screens/components/nav-tabs/overview/images/navTabs_specs.png diff --git a/apps/website/screens/components/nav-tabs/specs/images/navTabs_states.png b/apps/website/screens/components/nav-tabs/overview/images/navTabs_states.png similarity index 100% rename from apps/website/screens/components/nav-tabs/specs/images/navTabs_states.png rename to apps/website/screens/components/nav-tabs/overview/images/navTabs_states.png diff --git a/apps/website/screens/components/nav-tabs/specs/NavTabsSpecsPage.tsx b/apps/website/screens/components/nav-tabs/specs/NavTabsSpecsPage.tsx deleted file mode 100644 index 9ae7affb4d..0000000000 --- a/apps/website/screens/components/nav-tabs/specs/NavTabsSpecsPage.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcTable, DxcParagraph } from "@dxc-technology/halstack-react"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import Image from "@/common/Image"; -import Code from "@/common/Code"; -import navTabsSpecs from "./images/navTabs_specs.png"; -import navTabsAnatomy from "./images/navTabs_anatomy.png"; -import navTabsStates from "./images/navTabs_states.png"; - -const sections = [ - { - title: "Specifications", - content: ( -
- Nav tabs design specifications -
- ), - }, - { - title: "States", - content: ( - <> - - Tabs can get different states based on user interaction. These states are: enabled,{" "} - hover, focus, active and disabled. - -
- Nav tabs states -
- - ), - }, - { - title: "Anatomy", - content: ( - <> - Nav tabs anatomy - - Container - Default text label - Selected tab indicator - Default tab indicator - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - selectedBackgroundColor - - Tab item background:selected - - color-white - - #ffffff - - - - unselectedBackgroundColor - - Tab item background:enabled - - color-white - - #ffffff - - - - hoverBackgroundColor - - Tab item background:hover - - color-grey-100 - - #f2f2f2 - - - - pressedBackgroundColor - - Tab item background:active - - color-grey-200 - - #e6e6e6 - - - - selectedFontColor - - Label - - color-grey-700 - - #666666 - - - - unselectedFontColor - - Label - - color-grey-700 - - #666666 - - - - disabledFontColor - - Label:disabled - - color-grey-500 - - #999999 - - - - selectedIconColor - - Icon - - color-grey-700 - - #666666 - - - - unselectedIconColor - - Icon - - color-grey-700 - - #666666 - - - - disabledIconColor - - Icon:disabled - - color-grey-500 - - #999999 - - - - focusOutline - - Tab item outline - - color-blue-600 - - #0095ff - - - - selectedUnderlineColor - - Tab item border botton - - color-purple-700 - - #5f249f - - - - dividerColor - - Separator - - color-grey-400 - - #bfbfbf - - - - ), - }, - { - title: "Typography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - fontFamily - - Title - - font-family-sans - - 'Open Sans', sans-serif - - - - fontSize - - Title - - font-scale-03 - - 1rem / 16px - - - - fontStyle - - Title - - font-style-normal - - normal - - - - fontWeight - - Title - - font-weight-regular - - 400 - - - - ), - }, - ], - }, -]; - -const NavTabsSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default NavTabsSpecsPage; diff --git a/apps/website/screens/components/nav-tabs/specs/images/navTabs_anatomy.png b/apps/website/screens/components/nav-tabs/specs/images/navTabs_anatomy.png deleted file mode 100644 index 3f03145c9b..0000000000 Binary files a/apps/website/screens/components/nav-tabs/specs/images/navTabs_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/nav-tabs/usage/NavTabsUsagePage.tsx b/apps/website/screens/components/nav-tabs/usage/NavTabsUsagePage.tsx deleted file mode 100644 index bd9704ed79..0000000000 --- a/apps/website/screens/components/nav-tabs/usage/NavTabsUsagePage.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { DxcBulletedList, DxcFlex, DxcParagraph, DxcLink } from "@dxc-technology/halstack-react"; -import DocFooter from "@/common/DocFooter"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import Figure from "@/common/Figure"; -import Image from "@/common/Image"; -import navTabsNavigation from "./images/navTabs_navigation.png"; - -const sections = [ - { - title: "Usage", - content: ( - <> - - Nav tabs organize and allow navigation across different pages of content that are related and at the same - level of hierarchy. They are commonly used in the header of a page. - - - - Use nav tabs for page or site navigation when dividing content into different pages. Please take into - account the site navigation as the main difference between nav tabs and regular tabs in order to use them. - - The content should follow an organized structure and hierarchy. - Text labels should be brief and clearly related to the content. - - - For general tabs usage, refer to the{" "} - tabs usage section. - - - ), - }, - { - title: "Navigation", - content: ( - <> - - Nav tabs can be used both to navigate within a page as well as to external pages and links. - -
- Nav tabs navigation combination of internal and external links -
- - ), - }, -]; - -const NavTabsUsagePage = () => { - return ( - - - - - - - ); -}; - -export default NavTabsUsagePage; diff --git a/apps/website/screens/components/nav-tabs/usage/images/navTabs_navigation.png b/apps/website/screens/components/nav-tabs/usage/images/navTabs_navigation.png deleted file mode 100644 index 287765788d..0000000000 Binary files a/apps/website/screens/components/nav-tabs/usage/images/navTabs_navigation.png and /dev/null differ diff --git a/packages/lib/src/nav-tabs/NavTabs.stories.tsx b/packages/lib/src/nav-tabs/NavTabs.stories.tsx index 1d1c48e82d..77bd08ac54 100644 --- a/packages/lib/src/nav-tabs/NavTabs.stories.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.stories.tsx @@ -2,7 +2,6 @@ import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcContainer from "../container/Container"; -import { HalstackProvider } from "../HalstackContext"; import DxcNavTabs from "./NavTabs"; export default { @@ -21,13 +20,6 @@ const favoriteIcon = "filled_Favorite"; const pinIcon = "Location_On"; -const opinionatedTheme = { - navTabs: { - baseColor: "#666666", - accentColor: "#5f249f", - }, -}; - const NavTabs = () => ( <> @@ -69,7 +61,7 @@ const NavTabs = () => ( Tab 4 - + <DxcNavTabs> <DxcNavTabs.Tab href="#" active> @@ -196,85 +188,6 @@ const NavTabs = () => ( </DxcNavTabs> </ExampleContainer> <Title title="Opinionated theme" theme="light" level={2} /> - <ExampleContainer> - <Title title="Only label" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcNavTabs> - <DxcNavTabs.Tab href="#" active> - Tab 1 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" disabled> - Tab 2 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 3</DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 4</DxcNavTabs.Tab> - </DxcNavTabs> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered tabs" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcNavTabs> - <DxcNavTabs.Tab href="#" active> - Tab 1 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" disabled> - Tab 2 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 3</DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 4</DxcNavTabs.Tab> - </DxcNavTabs> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-focus"> - <Title title="Focused tabs" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcNavTabs> - <DxcNavTabs.Tab href="#" active> - Tab 1 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" disabled> - Tab 2 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 3</DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 4</DxcNavTabs.Tab> - </DxcNavTabs> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Actived tabs" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcNavTabs> - <DxcNavTabs.Tab href="#" active> - Tab 1 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" disabled> - Tab 2 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 3</DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#">Tab 4</DxcNavTabs.Tab> - </DxcNavTabs> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <Title title="With icon and notification number" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcNavTabs> - <DxcNavTabs.Tab href="#" active icon={favoriteIcon} notificationNumber> - Tab 1 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" disabled icon={favoriteIcon} notificationNumber={5}> - Tab 2 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" icon={favoriteIcon} notificationNumber={120}> - Tab 3 - </DxcNavTabs.Tab> - <DxcNavTabs.Tab href="#" icon={favoriteIcon} notificationNumber={12}> - Tab 4 - </DxcNavTabs.Tab> - </DxcNavTabs> - </HalstackProvider> - </ExampleContainer> <ExampleContainer> <Title title="NavTabs in a limited space container" theme="light" level={4} /> <DxcContainer width="500px"> diff --git a/packages/lib/src/nav-tabs/NavTabs.tsx b/packages/lib/src/nav-tabs/NavTabs.tsx index d37a57f620..e9cf9d1b64 100644 --- a/packages/lib/src/nav-tabs/NavTabs.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.tsx @@ -1,6 +1,5 @@ -import { Children, KeyboardEvent, ReactElement, ReactNode, useContext, useEffect, useMemo, useRef, useState } from "react"; -import styled, { ThemeProvider } from "styled-components"; -import HalstackContext from "../HalstackContext"; +import { Children, KeyboardEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from "react"; +import styled from "styled-components"; import NavTabsPropsType from "./types"; import DxcTab from "./Tab"; import NavTabsContext from "./NavTabsContext"; @@ -45,11 +44,27 @@ const getNextTabIndex = (array: ReactElement[], initialIndex: number): number => return index; }; +const Underline = styled.div<{ underlineWidth: number }>` + position: absolute; + bottom: 0; + left: 0; + height: var(--border-width-m); + background-color: var(--border-color-neutral-medium); + z-index: -1; + width: ${(props) => props.underlineWidth}px; +`; + +const NavTabsContainer = styled.div` + display: flex; + position: relative; + overflow: auto; + z-index: 0; +`; + const DxcNavTabs = ({ iconPosition = "top", tabIndex = 0, children }: NavTabsPropsType): JSX.Element => { const [innerFocusIndex, setInnerFocusIndex] = useState<number | null>(null); const [underlineWidth, setUnderlineWidth] = useState<number | null>(null); const refNavTabList = useRef<HTMLDivElement | null>(null); - const colorsTheme = useContext(HalstackContext); const childArray = Children.toArray(children).filter( (child) => typeof child === "object" && "props" in child @@ -88,32 +103,13 @@ const DxcNavTabs = ({ iconPosition = "top", tabIndex = 0, children }: NavTabsPro }; return ( - <ThemeProvider theme={colorsTheme.navTabs}> - <NavTabsContainer onKeyDown={handleOnKeyDown} ref={refNavTabList} role="tablist" aria-label="Navigation tabs"> - <NavTabsContext.Provider value={contextValue}>{children}</NavTabsContext.Provider> - <Underline underlineWidth={underlineWidth ?? 0} /> - </NavTabsContainer> - </ThemeProvider> + <NavTabsContainer onKeyDown={handleOnKeyDown} ref={refNavTabList} role="tablist" aria-label="Navigation tabs"> + <NavTabsContext.Provider value={contextValue}>{children}</NavTabsContext.Provider> + <Underline underlineWidth={underlineWidth ?? 0} /> + </NavTabsContainer> ); }; -const Underline = styled.div<{ underlineWidth: number }>` - position: absolute; - bottom: 0; - left: 0; - height: 2px; - background-color: ${(props) => props.theme.dividerColor}; - z-index: -1; - width: ${(props) => props.underlineWidth}px; -`; - DxcNavTabs.Tab = DxcTab; -const NavTabsContainer = styled.div` - display: flex; - position: relative; - overflow: auto; - z-index: 0; -`; - export default DxcNavTabs; diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx index 076c0f5d49..5aec52514b 100644 --- a/packages/lib/src/nav-tabs/Tab.tsx +++ b/packages/lib/src/nav-tabs/Tab.tsx @@ -6,6 +6,79 @@ import NavTabsPropsType, { TabProps } from "./types"; import NavTabsContext from "./NavTabsContext"; import DxcIcon from "../icon/Icon"; +const TabContainer = styled.div<{ active: TabProps["active"] }>` + align-items: stretch; + border-bottom: var(--border-width-s) var(--border-style-default) + ${(props) => (props.active ? "var(--border-color-primary-stronger)" : "transparent")}; + padding: var(--spacing-padding-xs); +`; + +const Tab = styled.a<{ + disabled: TabProps["disabled"]; + hasIcon: boolean; + iconPosition: NavTabsPropsType["iconPosition"]; +}>` + box-sizing: border-box; + display: flex; + flex-direction: ${(props) => (props.hasIcon && props.iconPosition === "top" ? "column" : "row")}; + justify-content: center; + align-items: center; + gap: var(--spacing-gap-xs); + height: ${(props) => (props.hasIcon && props.iconPosition === "top" ? "78px" : "100%")}; + min-width: 176px; + min-height: 48px; + padding: var(--spacing-padding-none) var(--spacing-padding-xs); + border-radius: var(--border-radius-s); + background: var(--color-bg-neutral-lightest); + text-decoration-color: transparent; + text-decoration-line: none; + cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; + + ${(props) => + !props.disabled && + ` + :hover { + background: var(--color-bg-primary-lighter); + } + :focus { + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + outline-offset: var(--border-width-m); + } + :active { + background: var(--color-bg-primary-lighter); + } + `} +`; + +const Label = styled.span<{ + disabled: TabProps["disabled"]; + active: TabProps["active"]; +}>` + display: inline; + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-stronger)")}; + font-family: var(--typography-font-family); + font-size: var(--typography-label-l); + font-weight: var(--typography-label-semibold); + text-align: center; + text-decoration: none; + text-overflow: unset; + white-space: normal; +`; + +const TabIconContainer = styled.div<{ + iconPosition: NavTabsPropsType["iconPosition"]; + active: TabProps["active"]; + disabled: TabProps["disabled"]; +}>` + display: flex; + font-size: var(--height-s); + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium)" : "var(--color-fg-neutral-stronger)")}; + svg { + height: var(--height-s); + width: 24px; + } +`; + const DxcTab = forwardRef( ( { href, active = false, icon, disabled = false, notificationNumber = false, children, ...otherProps }: TabProps, @@ -39,7 +112,6 @@ const DxcTab = forwardRef( <Tab href={!disabled ? href : undefined} disabled={disabled} - active={active} iconPosition={iconPosition} hasIcon={icon != null} ref={(anchorRef: HTMLAnchorElement) => { @@ -65,7 +137,7 @@ const DxcTab = forwardRef( {typeof icon === "string" ? <DxcIcon icon={icon} /> : icon} </TabIconContainer> )} - <DxcFlex alignItems="center" gap="0.5rem"> + <DxcFlex alignItems="center" gap="var(--spacing-gap-s)"> <Label active={active} disabled={disabled}> {children} </Label> @@ -83,92 +155,4 @@ const DxcTab = forwardRef( } ); -const TabContainer = styled.div<{ active: TabProps["active"] }>` - align-items: stretch; - border-bottom: 2px solid ${(props) => (props.active ? props.theme.selectedUnderlineColor : "transparent")}; - padding: 0.5rem; -`; - -const Tab = styled.a<{ - disabled: TabProps["disabled"]; - active: TabProps["active"]; - hasIcon: boolean; - iconPosition: NavTabsPropsType["iconPosition"]; -}>` - box-sizing: border-box; - display: flex; - flex-direction: ${(props) => (props.hasIcon && props.iconPosition === "top" ? "column" : "row")}; - justify-content: center; - align-items: center; - gap: ${(props) => (props.hasIcon && props.iconPosition === "top" ? "0.375rem" : "0.625rem")}; - height: ${(props) => (props.hasIcon && props.iconPosition === "top" ? "78px" : "100%")}; - min-width: 176px; - min-height: 44px; - padding: 0.375rem; - border-radius: 4px; - background: ${(props) => - props.active ? props.theme.selectedBackgroundColor : props.theme.unselectedBackgroundColor}; - text-decoration-color: transparent; - text-decoration-line: none; - cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; - - ${(props) => - !props.disabled && - ` - :hover { - background: ${props.theme.hoverBackgroundColor}; - } - :focus { - outline: 2px solid ${props.theme.focusOutline}; - } - :active { - background: ${props.theme.pressedBackgroundColor}; - outline: 2px solid #33aaff}; - } - `} -`; - -const Label = styled.span<{ - disabled: TabProps["disabled"]; - active: TabProps["active"]; -}>` - display: inline; - color: ${(props) => - props.disabled - ? props.theme.disabledFontColor - : props.active - ? props.theme.selectedFontColor - : props.theme.unselectedFontColor}; - font-family: ${(props) => props.theme.fontFamily}; - font-size: ${(props) => props.theme.fontSize}; - font-style: ${(props) => props.theme.fontStyle}; - font-weight: ${(props) => props.theme.fontWeight}; - text-align: center; - letter-spacing: 0.025em; - line-height: 1.715em; - text-decoration: none; - text-overflow: unset; - white-space: normal; - margin: 0; -`; - -const TabIconContainer = styled.div<{ - iconPosition: NavTabsPropsType["iconPosition"]; - active: TabProps["active"]; - disabled: TabProps["disabled"]; -}>` - display: flex; - font-size: 24px; - color: ${(props) => - props.active - ? props.theme.selectedIconColor - : props.disabled - ? props.theme.disabledIconColor - : props.theme.unselectedIconColor}; - svg { - height: 24px; - width: 24px; - } -`; - export default DxcTab;