diff --git a/.changeset/swift-forks-drop.md b/.changeset/swift-forks-drop.md
new file mode 100644
index 0000000000..e429584679
--- /dev/null
+++ b/.changeset/swift-forks-drop.md
@@ -0,0 +1,6 @@
+---
+"@digdir/designsystemet-css": patch
+"@digdir/designsystemet-react": patch
+---
+
+Breadcrumbs: Rename `Breadcrumbs.Root` to `Breadcrumbs` and remove `Breadcrumbs.Nav`
diff --git a/packages/css/breadcrumbs.css b/packages/css/breadcrumbs.css
index df85e2bc75..7081f3d3aa 100644
--- a/packages/css/breadcrumbs.css
+++ b/packages/css/breadcrumbs.css
@@ -1,72 +1,63 @@
.ds-breadcrumbs {
--dsc-breadcrumbs-spacing: var(--ds-spacing-2);
--dsc-breadcrumbs-chevron-size: var(--ds-sizing-6);
- --dsc-breadcrumbs-link-color: inherit;
-}
-
-.ds-breadcrumbs--sm {
- --dsc-breadcrumbs-spacing: var(--ds-spacing-1);
- --dsc-breadcrumbs-chevron-size: var(--ds-sizing-5);
-}
-.ds-breadcrumbs--md {
- --dsc-breadcrumbs-spacing: var(--ds-spacing-2);
- --dsc-breadcrumbs-chevron-size: var(--ds-sizing-6);
-}
-
-.ds-breadcrumbs--lg {
- --dsc-breadcrumbs-spacing: var(--ds-spacing-3);
- --dsc-breadcrumbs-chevron-size: var(--ds-sizing-7);
-}
+ &[data-size='sm'] {
+ --dsc-breadcrumbs-spacing: var(--ds-spacing-1);
+ --dsc-breadcrumbs-chevron-size: var(--ds-sizing-5);
+ }
-.ds-breadcrumbs__list {
- display: flex;
- flex-wrap: wrap;
- list-style-type: none;
- margin: 0;
- padding: 0;
- gap: var(--dsc-breadcrumbs-spacing) 0;
-}
+ &[data-size='lg'] {
+ --dsc-breadcrumbs-spacing: var(--ds-spacing-3);
+ --dsc-breadcrumbs-chevron-size: var(--ds-sizing-7);
+ }
-.ds-breadcrumbs__item:where(:not(:last-child))::after,
-.ds-breadcrumbs > .ds-breadcrumbs__link::before {
- background: currentcolor;
- content: '';
- display: inline-block;
- height: var(--dsc-breadcrumbs-chevron-size);
- margin-inline: var(--dsc-breadcrumbs-spacing);
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E")
- 50% / contain no-repeat;
- vertical-align: middle;
- width: var(--dsc-breadcrumbs-chevron-size);
-}
+ & > :is(ol, ul) {
+ display: flex;
+ flex-wrap: wrap;
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ gap: var(--dsc-breadcrumbs-spacing) 0;
+ }
-/* When link is direct child of Breadcrumbs, make it back button */
-.ds-breadcrumbs > .ds-breadcrumbs__link::before {
- margin: 0;
- rotate: 180deg;
-}
+ & a:not(:focus-visible) {
+ color: inherit;
+ }
-.ds-breadcrumbs__link {
- --dsc-link-color: var(--dsc-breadcrumbs-link-color);
- --dsc-link-color-visited: var(--dsc-breadcrumbs-link-color);
-}
+ & a[aria-current='page'] {
+ text-decoration: none;
+ }
-.ds-breadcrumbs__link[aria-current='page'] {
- text-decoration: none;
-}
+ /* Draw chevron between items and before back link */
+ & li:where(:not(:last-child))::after,
+ & > :not(ol, ul)::before {
+ background: currentcolor;
+ content: '';
+ display: inline-block;
+ height: var(--dsc-breadcrumbs-chevron-size);
+ margin-inline: var(--dsc-breadcrumbs-spacing);
+ mask: center / contain no-repeat
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath d='M9.47 5.97a.75.75 0 0 1 1.06 0l5.5 5.5a.75.75 0 0 1 0 1.06l-5.5 5.5a.75.75 0 1 1-1.06-1.06L14.44 12 9.47 7.03a.75.75 0 0 1 0-1.06'/%3E%3C/svg%3E");
+ vertical-align: middle;
+ width: var(--dsc-breadcrumbs-chevron-size);
+ }
-.ds-breadcrumbs > .ds-breadcrumbs__link:where(:not(:only-child)) {
- display: none;
-}
+ /* When link is direct child of Breadcrumbs, make it back button */
+ & > :not(ol, ul)::before {
+ margin: 0;
+ rotate: 180deg;
+ }
-@media (max-width: 650px) {
- .ds-breadcrumbs > .ds-breadcrumbs__nav:where(:not(:only-child)) {
- display: none;
+ @media (max-width: 650px) {
+ & > :is(ol, ul):not(:only-child) {
+ display: none; /* Hide list when mobile and having back link */
+ }
}
- .ds-breadcrumbs > .ds-breadcrumbs__link {
- display: block;
- width: fit-content;
+ @media (min-width: 651px) {
+ & > :not(ol, ul) {
+ display: none; /* Hide back link when desktop and having list */
+ }
}
}
diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx
index 4e51412621..d406c32781 100644
--- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx
+++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.mdx
@@ -17,38 +17,36 @@ hvor de er i en struktur, for eksempel på en nettside. Da kan de lettere bytte
Den siste lenken i brødsmulestien blir automatisk markert med `aria-current="page"`.
```tsx
-
+
Nivå 3
-
-
-
- Nivå 1
-
-
- Nivå 2
-
-
-
-
+
+
+ Nivå 1
+
+
+ Nivå 2
+
+
+
```
## Varianter av `Breadcrumb`
### Kun tilbake-knapp
-Hvis du legger en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs.Root`, vil denne lenken vises som en tilbake-knapp. Det er viktig at du er konsistent i din løsning og bruker enten tilbake-knapp eller sti.
+Hvis du legger en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs`, vil denne lenken vises som en tilbake-knapp. Det er viktig at du er konsistent i din løsning og bruker enten tilbake-knapp eller sti.
### Kun sti
-Hvis du legger en `Breadcrumbs.List` som direkte barn av `Breadcrumbs.Root`, vil denne vises som en sti.
+Hvis du legger en `Breadcrumbs.List` som direkte barn av `Breadcrumbs`, vil denne vises som en sti.
### Både sti på desktop og tilbake-knapp på mobil
-Hvis du legger både en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs.Root` og en `Breadcrumbs.List`, vil tilbake-knapp vises på mobil, og sti vises på desktop.
+Hvis du legger både en `Breadcrumbs.Link` som direkte barn av `Breadcrumbs` og en `Breadcrumbs.List`, vil tilbake-knapp vises på mobil, og sti vises på desktop.
diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx
index 50a6aa81c5..80f7e84192 100644
--- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx
+++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.stories.tsx
@@ -4,18 +4,18 @@ import { Breadcrumbs } from '.';
export default {
title: 'Komponenter/Breadcrumbs',
- component: Breadcrumbs.Root,
+ component: Breadcrumbs,
args: {
size: 'md',
},
} as Meta;
-export const Preview: StoryFn = (args) => (
-
-
- Nivå 3
-
-
+export const Preview: StoryFn = (args) => (
+ <>
+
+
+ Nivå 3
+
Nivå 1
@@ -30,107 +30,99 @@ export const Preview: StoryFn = (args) => (
Nivå 4
-
-
+
+ >
);
-export const ListOnly: StoryFn = (args) => (
-
-
-
-
- Nivå 1
-
-
- Nivå 2
-
-
- Nivå 3
-
-
- Nivå 4
-
-
-
-
+export const ListOnly: StoryFn = (args) => (
+
+
+
+ Nivå 1
+
+
+ Nivå 2
+
+
+ Nivå 3
+
+
+ Nivå 4
+
+
+
);
-export const BackOnly: StoryFn = (args) => (
-
+export const BackOnly: StoryFn = (args) => (
+
Nivå 3
-
+
);
-export const LongItems: StoryFn = (args) => (
-
+export const LongItems: StoryFn = (args) => (
+
Slik søker du om helsesertifikat for sjømat
-
-
-
- Hjem
-
-
-
- Eksport til land utenfor EU/EØS
-
-
-
- Eksport av mat og drikke
-
-
-
- Eksport av fisk og sjømat
-
-
-
-
- Veiledning om helsesertifikat for sjømat
-
-
-
-
- Slik søker du om helsesertifikat for sjømat
-
-
-
-
- Slik søker du om helsesertifikat i ny eksportløsning
-
-
-
-
-
+
+
+ Hjem
+
+
+
+ Eksport til land utenfor EU/EØS
+
+
+
+ Eksport av mat og drikke
+
+
+ Eksport av fisk og sjømat
+
+
+
+ Veiledning om helsesertifikat for sjømat
+
+
+
+
+ Slik søker du om helsesertifikat for sjømat
+
+
+
+
+ Slik søker du om helsesertifikat i ny eksportløsning
+
+
+
+
);
-export const MobileViewport: StoryFn = (args) => (
-
+export const MobileViewport: StoryFn = (args) => (
+
Nivå 3
-
-
-
- Nivå 1
-
-
- Nivå 2
-
-
- Nivå 3
-
-
- Nivå 4
-
-
-
-
+
+
+ Nivå 1
+
+
+ Nivå 2
+
+
+ Nivå 3
+
+
+ Nivå 4
+
+
+
);
MobileViewport.parameters = {
diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx
index c2e7159ba4..dff199e5bc 100644
--- a/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx
+++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.test.tsx
@@ -1,15 +1,15 @@
import { render, screen } from '@testing-library/react';
-import type { BreadcrumbsRootProps } from './BreadcrumbsRoot';
+import type { BreadcrumbsProps } from './Breadcrumbs';
import { Breadcrumbs } from './';
-const renderWithRoot = (props?: BreadcrumbsRootProps) =>
+const renderWithRoot = (props?: BreadcrumbsProps) =>
render(
-
-
- Nivå 3
-
-
+ <>
+
+
+ Nivå 3
+
Nivå 1
@@ -24,11 +24,11 @@ const renderWithRoot = (props?: BreadcrumbsRootProps) =>
Nivå 4
-
- ,
+
+ >,
);
-describe('Breadcrumbs.Root', () => {
+describe('Breadcrumbs', () => {
it('should render correctly with default props', () => {
renderWithRoot();
@@ -58,30 +58,28 @@ describe('Breadcrumbs.List', () => {
// Re-render with additional level
render(
-
+
Nivå 3
-
-
-
- Nivå 1
-
-
- Nivå 2
-
-
- Nivå 3
-
-
- Nivå 4
-
-
- Nivå 5
-
-
-
- ,
+
+
+ Nivå 1
+
+
+ Nivå 2
+
+
+ Nivå 3
+
+
+ Nivå 4
+
+
+ Nivå 5
+
+
+ ,
);
expect(links.at(-2)).not.toHaveAttribute('aria-current', 'page');
diff --git a/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx
new file mode 100644
index 0000000000..c9caf11a2f
--- /dev/null
+++ b/packages/react/src/components/Breadcrumbs/Breadcrumbs.tsx
@@ -0,0 +1,33 @@
+import cl from 'clsx/lite';
+import { type HTMLAttributes, forwardRef } from 'react';
+import { Paragraph } from '../Typography';
+
+export type BreadcrumbsProps = {
+ /**
+ * Sets the screen reader label for the Breadcrumbs area
+ * @default 'Du er her'
+ */
+ 'aria-label'?: string;
+ /**
+ * Sets the size of the component
+ * @default md
+ */
+ size?: 'sm' | 'md' | 'lg';
+} & HTMLAttributes;
+
+export const Breadcrumbs = forwardRef(
+ (
+ { 'aria-label': ariaLabel = 'Du er her:', className, size, ...rest },
+ ref,
+ ) => (
+
+
+
+ ),
+);
diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsItem.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsItem.tsx
index 336928df5f..9e54550ba6 100644
--- a/packages/react/src/components/Breadcrumbs/BreadcrumbsItem.tsx
+++ b/packages/react/src/components/Breadcrumbs/BreadcrumbsItem.tsx
@@ -1,12 +1,9 @@
-import cl from 'clsx/lite';
import { type HTMLAttributes, forwardRef } from 'react';
export type BreadcrumbsItemProps = HTMLAttributes;
export const BreadcrumbsItem = forwardRef(
- ({ className, ...rest }, ref) => (
-
- ),
+ function BreadcrumbsItem({ className, ...rest }, ref) {
+ return ;
+ },
);
-
-BreadcrumbsItem.displayName = 'BreadcrumbsItem';
diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsLink.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsLink.tsx
index cebc8ab45f..2c2ca5b9ba 100644
--- a/packages/react/src/components/Breadcrumbs/BreadcrumbsLink.tsx
+++ b/packages/react/src/components/Breadcrumbs/BreadcrumbsLink.tsx
@@ -1,6 +1,4 @@
-import { useMergeRefs } from '@floating-ui/react';
-import cl from 'clsx/lite';
-import { forwardRef, useRef } from 'react';
+import { forwardRef } from 'react';
import { Link } from '../Link';
import type { LinkProps } from '../Link';
@@ -10,8 +8,6 @@ export type BreadcrumbsLinkProps = LinkProps;
export const BreadcrumbsLink = forwardRef<
HTMLAnchorElement,
BreadcrumbsLinkProps
->(({ className, ...rest }, ref) => (
-
-));
-
-BreadcrumbsLink.displayName = 'BreadcrumbsLink';
+>(function BreadcrumbsLink(rest, ref) {
+ return ;
+});
diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsList.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsList.tsx
index 951090b328..d50e2afd19 100644
--- a/packages/react/src/components/Breadcrumbs/BreadcrumbsList.tsx
+++ b/packages/react/src/components/Breadcrumbs/BreadcrumbsList.tsx
@@ -1,5 +1,4 @@
import { useMergeRefs } from '@floating-ui/react';
-import cl from 'clsx/lite';
import { type HTMLAttributes, forwardRef, useEffect, useRef } from 'react';
export type BreadcrumbsListProps = HTMLAttributes;
@@ -7,26 +6,18 @@ export type BreadcrumbsListProps = HTMLAttributes;
export const BreadcrumbsList = forwardRef<
HTMLOListElement,
BreadcrumbsListProps
->(({ className, ...rest }, ref) => {
+>(function BreadcrumbsList(rest, ref) {
const innerRef = useRef(null);
const mergedRefs = useMergeRefs([innerRef, ref]);
// Set aria-current on last link
useEffect(() => {
- const links = innerRef.current?.querySelectorAll('a') || [];
+ const links = innerRef.current?.querySelectorAll(':scope > * > *') || [];
const lastLink = links[links?.length - 1];
lastLink?.setAttribute('aria-current', 'page');
return () => lastLink?.removeAttribute('aria-current'); // Remove on re-render as React can re-use DOM elements
});
- return (
-
- );
+ return
;
});
-
-BreadcrumbsList.displayName = 'BreadcrumbsList';
diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx
deleted file mode 100644
index 7b050ea7d0..0000000000
--- a/packages/react/src/components/Breadcrumbs/BreadcrumbsNav.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import cl from 'clsx/lite';
-import { type HTMLAttributes, forwardRef } from 'react';
-
-export type BreadcrumbsNavProps = {
- /**
- * Sets the screen reader label for the Breadcrumbs area
- * @default 'Du er her'
- */
- 'aria-label'?: string;
-} & HTMLAttributes;
-
-export const BreadcrumbsNav = forwardRef(
- ({ 'aria-label': ariaLabel = 'Du er her:', className, ...rest }, ref) => (
-
- ),
-);
-
-BreadcrumbsNav.displayName = 'BreadcrumbsNav';
diff --git a/packages/react/src/components/Breadcrumbs/BreadcrumbsRoot.tsx b/packages/react/src/components/Breadcrumbs/BreadcrumbsRoot.tsx
deleted file mode 100644
index a78527d5d1..0000000000
--- a/packages/react/src/components/Breadcrumbs/BreadcrumbsRoot.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import cl from 'clsx/lite';
-import { type HTMLAttributes, forwardRef } from 'react';
-
-export type BreadcrumbsRootProps = {
- /**
- * Sets the size of the component
- * @default md
- */
- size?: 'sm' | 'md' | 'lg';
-} & HTMLAttributes;
-
-export const BreadcrumbsRoot = forwardRef(
- ({ className, size = 'md', ...rest }, ref) => (
-
- ),
-);
-
-BreadcrumbsRoot.displayName = 'BreadcrumbsRoot';
diff --git a/packages/react/src/components/Breadcrumbs/index.ts b/packages/react/src/components/Breadcrumbs/index.ts
index fcd3ce739f..46f4f28c77 100644
--- a/packages/react/src/components/Breadcrumbs/index.ts
+++ b/packages/react/src/components/Breadcrumbs/index.ts
@@ -1,49 +1,33 @@
+import { Breadcrumbs as BreadcrumbsParent } from './Breadcrumbs';
import { BreadcrumbsItem } from './BreadcrumbsItem';
import { BreadcrumbsLink } from './BreadcrumbsLink';
import { BreadcrumbsList } from './BreadcrumbsList';
-import { BreadcrumbsNav } from './BreadcrumbsNav';
-import { BreadcrumbsRoot } from './BreadcrumbsRoot';
-
-export type { BreadcrumbsRootProps } from './BreadcrumbsRoot';
-export type { BreadcrumbsNavProps } from './BreadcrumbsNav';
-export type { BreadcrumbsListProps } from './BreadcrumbsList';
-export type { BreadcrumbsItemProps } from './BreadcrumbsItem';
-export type { BreadcrumbsLinkProps } from './BreadcrumbsLink';
/**
* Breadcrumbs are used to visualize the current page path.
* @example
- *
+ *
* Level 2
- *
- *
- * Level 1
- * Level 2
- * Level 3
- * Level 4
- *
- *
- *
+ *
+ * Level 1
+ * Level 2
+ * Level 3
+ * Level 4
+ *
+ *
*/
-const Breadcrumbs = {
- Root: BreadcrumbsRoot,
- Nav: BreadcrumbsNav,
+export const Breadcrumbs = Object.assign(BreadcrumbsParent, {
List: BreadcrumbsList,
Item: BreadcrumbsItem,
Link: BreadcrumbsLink,
-};
+});
-Breadcrumbs.Root.displayName = 'Breadcrumbs.Root';
-Breadcrumbs.Nav.displayName = 'Breadcrumbs.Nav';
Breadcrumbs.List.displayName = 'Breadcrumbs.List';
Breadcrumbs.Item.displayName = 'Breadcrumbs.Item';
Breadcrumbs.Link.displayName = 'Breadcrumbs.Link';
-export {
- Breadcrumbs,
- BreadcrumbsRoot,
- BreadcrumbsNav,
- BreadcrumbsList,
- BreadcrumbsItem,
- BreadcrumbsLink,
-};
+export type { BreadcrumbsProps } from './Breadcrumbs';
+export type { BreadcrumbsListProps } from './BreadcrumbsList';
+export type { BreadcrumbsItemProps } from './BreadcrumbsItem';
+export type { BreadcrumbsLinkProps } from './BreadcrumbsLink';
+export { BreadcrumbsList, BreadcrumbsItem, BreadcrumbsLink };