diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 34c267b981..4cab821b11 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -5,10 +5,8 @@ import React, { useLayoutEffect } from "react"; import darksideCss from "../@navikt/core/css/darkside/index.css?inline"; // @ts-expect-error - Temporary import defaultCss from "../@navikt/core/css/index.css?inline"; -import { - Provider, - UNSAFE_AkselTheme, -} from "../@navikt/core/react/src/provider"; +import { Provider } from "../@navikt/core/react/src/provider"; +import { Theme } from "../@navikt/core/react/src/theme"; import en from "../@navikt/core/react/src/util/i18n/locales/en"; import nb from "../@navikt/core/react/src/util/i18n/locales/nb"; import nn from "../@navikt/core/react/src/util/i18n/locales/nn"; @@ -31,9 +29,9 @@ const ModeDecorator = ({ children, mode, theme }) => { }, [mode]); return mode === "darkside" ? ( - + {children} - + ) : ( children ); diff --git a/@navikt/core/react/src/accordion/AccordionContent.tsx b/@navikt/core/react/src/accordion/AccordionContent.tsx index 30b125f0bb..7350ed9291 100644 --- a/@navikt/core/react/src/accordion/AccordionContent.tsx +++ b/@navikt/core/react/src/accordion/AccordionContent.tsx @@ -1,6 +1,6 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; -import { UNSAFE_useAkselTheme } from "../provider"; +import { useThemeInternal } from "../theme/Theme"; import { BodyLong } from "../typography"; import { AccordionItemContext } from "./AccordionItem"; @@ -16,7 +16,7 @@ const AccordionContent = forwardRef( ({ children, className, ...rest }, ref) => { const context = useContext(AccordionItemContext); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); if (context === null) { console.error( diff --git a/@navikt/core/react/src/accordion/AccordionHeader.tsx b/@navikt/core/react/src/accordion/AccordionHeader.tsx index 98e26ae47f..b7dfaa8351 100644 --- a/@navikt/core/react/src/accordion/AccordionHeader.tsx +++ b/@navikt/core/react/src/accordion/AccordionHeader.tsx @@ -1,7 +1,7 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; import { ChevronDownIcon } from "@navikt/aksel-icons"; -import { UNSAFE_useAkselTheme } from "../provider"; +import { useThemeInternal } from "../theme/Theme"; import { Heading } from "../typography"; import { composeEventHandlers } from "../util/composeEventHandlers"; import { AccordionContext } from "./AccordionContext"; @@ -20,7 +20,7 @@ const AccordionHeader = forwardRef( const itemContext = useContext(AccordionItemContext); const accordionContext = useContext(AccordionContext); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); if (itemContext === null) { console.error( diff --git a/@navikt/core/react/src/date/datepicker/parts/WeekNumber.tsx b/@navikt/core/react/src/date/datepicker/parts/WeekNumber.tsx index 5a2ac8035b..38c22364c9 100644 --- a/@navikt/core/react/src/date/datepicker/parts/WeekNumber.tsx +++ b/@navikt/core/react/src/date/datepicker/parts/WeekNumber.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Button as RDPButton, useDayPicker } from "react-day-picker"; import { Button } from "../../../button"; -import { UNSAFE_useAkselTheme } from "../../../provider"; +import { useThemeInternal } from "../../../theme/Theme"; import { Detail } from "../../../typography"; import { useDateTranslationContext } from "../../context"; @@ -21,7 +21,7 @@ function WeekNumber({ dates, }: WeekNumberProps): JSX.Element { const { onWeekNumberClick, styles, classNames } = useDayPicker(); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const translate = useDateTranslationContext().translate; if (!onWeekNumberClick) { diff --git a/@navikt/core/react/src/form/search/Search.tsx b/@navikt/core/react/src/form/search/Search.tsx index ce77637ddd..b8a14faaaa 100644 --- a/@navikt/core/react/src/form/search/Search.tsx +++ b/@navikt/core/react/src/form/search/Search.tsx @@ -7,7 +7,7 @@ import React, { } from "react"; import { MagnifyingGlassIcon, XMarkIcon } from "@navikt/aksel-icons"; import { Button } from "../../button"; -import { UNSAFE_useAkselTheme } from "../../provider"; +import { useThemeInternal } from "../../theme/Theme"; import { BodyShort, ErrorMessage, Label } from "../../typography"; import { omit } from "../../util"; import { useMergeRefs } from "../../util/hooks/useMergeRefs"; @@ -125,7 +125,7 @@ export const Search = forwardRef( ...rest } = props; - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const searchRef = useRef(null); const mergedRef = useMergeRefs(searchRef, ref); diff --git a/@navikt/core/react/src/form/switch/Switch.tsx b/@navikt/core/react/src/form/switch/Switch.tsx index fe3d3bc385..b63133e4b8 100644 --- a/@navikt/core/react/src/form/switch/Switch.tsx +++ b/@navikt/core/react/src/form/switch/Switch.tsx @@ -6,7 +6,7 @@ import React, { useState, } from "react"; import { Loader } from "../../loader"; -import { UNSAFE_useAkselTheme } from "../../provider"; +import { useThemeInternal } from "../../theme/Theme"; import { BodyShort } from "../../typography"; import { omit } from "../../util"; import { ReadOnlyIconWithTitle } from "../ReadOnlyIcon"; @@ -69,7 +69,7 @@ export const Switch = forwardRef( defaultChecked ?? checkedProp ?? false, ); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); useEffect(() => { checkedProp !== undefined && setChecked(checkedProp); diff --git a/@navikt/core/react/src/guide-panel/GuidePanel.tsx b/@navikt/core/react/src/guide-panel/GuidePanel.tsx index a01abed4f8..b35ff6b5a9 100644 --- a/@navikt/core/react/src/guide-panel/GuidePanel.tsx +++ b/@navikt/core/react/src/guide-panel/GuidePanel.tsx @@ -1,6 +1,6 @@ import cl from "clsx"; import React, { HTMLAttributes, forwardRef } from "react"; -import { UNSAFE_useAkselTheme } from "../provider"; +import { useThemeInternal } from "../theme/Theme"; import { DefaultIllustration } from "./Illustration"; import { DarksideGudiepanelIllustration } from "./Illustration.darkside"; @@ -37,7 +37,7 @@ export interface GuidePanelProps extends HTMLAttributes { */ export const GuidePanel = forwardRef( ({ children, className, illustration, poster, ...rest }, ref) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); return (
( const buttonRef = useRef(null); const mergedRef = useMergeRefs(buttonRef, ref); const [open, setOpen] = useState(false); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const translate = useI18n("HelpText"); const titleWithFallback = title || translate("title"); diff --git a/@navikt/core/react/src/internal-header/InternalHeader.tsx b/@navikt/core/react/src/internal-header/InternalHeader.tsx index bb135378bb..654f7b5166 100644 --- a/@navikt/core/react/src/internal-header/InternalHeader.tsx +++ b/@navikt/core/react/src/internal-header/InternalHeader.tsx @@ -1,6 +1,6 @@ import cl from "clsx"; import React, { HTMLAttributes, forwardRef } from "react"; -import { UNSAFE_AkselTheme, UNSAFE_useAkselTheme } from "../provider"; +import { Theme, useThemeInternal } from "../theme/Theme"; import { OverridableComponent } from "../util/types"; import InternalHeaderButton, { InternalHeaderButtonProps, @@ -78,20 +78,20 @@ interface InternalHeaderComponent * ``` */ export const InternalHeader = forwardRef(({ className, ...rest }, ref) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); /* * Component is always in "dark" mode, so we manually override global theme. */ if (themeContext) { return ( - +
- + ); } diff --git a/@navikt/core/react/src/layout/base/BasePrimitive.tsx b/@navikt/core/react/src/layout/base/BasePrimitive.tsx index 064bd46f44..b679c2e8c8 100644 --- a/@navikt/core/react/src/layout/base/BasePrimitive.tsx +++ b/@navikt/core/react/src/layout/base/BasePrimitive.tsx @@ -1,7 +1,7 @@ import cl from "clsx"; import React from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { getResponsiveProps, getResponsiveValue } from "../utilities/css"; import { ResponsiveProp, SpacingScale } from "../utilities/types"; @@ -252,7 +252,7 @@ export const BasePrimitive = ({ flexShrink, gridColumn, }: BasePrimitiveProps) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const prefix = themeContext ? "ax" : "a"; const style: React.CSSProperties = { diff --git a/@navikt/core/react/src/layout/bleed/Bleed.tsx b/@navikt/core/react/src/layout/bleed/Bleed.tsx index 2aa5b83c73..4d292ff28f 100644 --- a/@navikt/core/react/src/layout/bleed/Bleed.tsx +++ b/@navikt/core/react/src/layout/bleed/Bleed.tsx @@ -1,7 +1,7 @@ import cl from "clsx"; import React, { forwardRef } from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { getResponsiveProps } from "../utilities/css"; import { ResponsiveProp, SpacingScale } from "../utilities/types"; @@ -81,7 +81,7 @@ export const Bleed = forwardRef( }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const prefix = themeContext ? "ax" : "a"; let style: React.CSSProperties = { diff --git a/@navikt/core/react/src/layout/box/Box.tsx b/@navikt/core/react/src/layout/box/Box.tsx index 0b79d1e2ab..9f218026aa 100644 --- a/@navikt/core/react/src/layout/box/Box.tsx +++ b/@navikt/core/react/src/layout/box/Box.tsx @@ -1,8 +1,8 @@ import cl from "clsx"; import React, { forwardRef } from "react"; import { type BorderRadiusKeys } from "@navikt/ds-tokens/types"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { omit } from "../../util"; import { OverridableComponent } from "../../util/types"; import BasePrimitive, { @@ -109,7 +109,7 @@ export const BoxComponent: OverridableComponent = }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); if ( process.env.NODE_ENV !== "production" && diff --git a/@navikt/core/react/src/layout/grid/HGrid.tsx b/@navikt/core/react/src/layout/grid/HGrid.tsx index e894968274..9809d80528 100644 --- a/@navikt/core/react/src/layout/grid/HGrid.tsx +++ b/@navikt/core/react/src/layout/grid/HGrid.tsx @@ -1,7 +1,7 @@ import cl from "clsx"; import React, { forwardRef } from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { OverridableComponent, omit } from "../../util"; import BasePrimitive, { PRIMITIVE_PROPS, @@ -78,7 +78,7 @@ export const HGrid: OverridableComponent = }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const prefix = themeContext ? "ax" : "a"; const styles: React.CSSProperties = { diff --git a/@navikt/core/react/src/layout/page/Page.tsx b/@navikt/core/react/src/layout/page/Page.tsx index 598ec3b4af..24bddf0726 100644 --- a/@navikt/core/react/src/layout/page/Page.tsx +++ b/@navikt/core/react/src/layout/page/Page.tsx @@ -1,6 +1,6 @@ import cl from "clsx"; import React, { forwardRef } from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; +import { useThemeInternal } from "../../theme/Theme"; import { OverridableComponent } from "../../util"; import { BackgroundColorToken } from "../utilities/types"; import { PageBlock } from "./parts/PageBlock"; @@ -54,7 +54,7 @@ export const PageComponent: OverridableComponent = }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); if (process.env.NODE_ENV !== "production" && themeContext && background) { console.warn( diff --git a/@navikt/core/react/src/layout/stack/Stack.tsx b/@navikt/core/react/src/layout/stack/Stack.tsx index 880b36cdb3..68b8cfed95 100644 --- a/@navikt/core/react/src/layout/stack/Stack.tsx +++ b/@navikt/core/react/src/layout/stack/Stack.tsx @@ -1,7 +1,7 @@ import cl from "clsx"; import React, { HTMLAttributes, forwardRef } from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { omit } from "../../util"; import { OverridableComponent } from "../../util/types"; import BasePrimitive, { @@ -84,7 +84,7 @@ export const Stack: OverridableComponent = }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const prefix = themeContext ? "ax" : "a"; const style: React.CSSProperties = { diff --git a/@navikt/core/react/src/overlays/action-menu/ActionMenu.tsx b/@navikt/core/react/src/overlays/action-menu/ActionMenu.tsx index c159cd0ccc..8d81e2bb13 100644 --- a/@navikt/core/react/src/overlays/action-menu/ActionMenu.tsx +++ b/@navikt/core/react/src/overlays/action-menu/ActionMenu.tsx @@ -2,8 +2,8 @@ import cl from "clsx"; import React, { forwardRef, useRef } from "react"; import { ChevronRightIcon } from "@navikt/aksel-icons"; import { useModalContext } from "../../modal/Modal.context"; -import { UNSAFE_useAkselTheme } from "../../provider"; import { Slot } from "../../slot/Slot"; +import { useThemeInternal } from "../../theme/Theme"; import { OverridableComponent, useId } from "../../util"; import { composeEventHandlers } from "../../util/composeEventHandlers"; import { createContext } from "../../util/create-context"; @@ -736,7 +736,7 @@ export const ActionMenuRadioItem = forwardRef< { children, className, onSelect, ...rest }: ActionMenuRadioItemProps, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); return ( { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); return (
-
-

BrandVolumeHigh

- {BrandVolumeHigh?.render?.(...props)} -
); }, diff --git a/@navikt/core/react/src/theme/Theme.tsx b/@navikt/core/react/src/theme/Theme.tsx new file mode 100644 index 0000000000..416c30c072 --- /dev/null +++ b/@navikt/core/react/src/theme/Theme.tsx @@ -0,0 +1,60 @@ +import cl from "clsx"; +import React, { forwardRef } from "react"; +import { Slot } from "../slot/Slot"; +import { createContext } from "../util/create-context"; +import { AsChildProps } from "../util/types"; + +type ThemeContext = { + /** + * Color theme + * @default "light" + */ + theme: "light" | "dark"; +}; + +const [ThemeProvider, useThemeInternal] = createContext({ + hookName: "useTheme", + name: "ThemeProvider", + providerName: "ThemeProvider", +}); + +type ThemeProps = { + className?: string; + hasBackground?: boolean; +} & ThemeContext & + AsChildProps; + +const Theme = forwardRef( + (props: ThemeProps, ref) => { + const context = useThemeInternal(false); + + const { + children, + className, + asChild = false, + theme = context?.theme ?? "light", + hasBackground: hasBackgroundProp = true, + } = props; + + const isRoot = context === undefined; + + const hasBackground = + hasBackgroundProp ?? (isRoot && props.theme !== undefined); + + const SlotElement = asChild ? Slot : "div"; + + return ( + + + {children} + + + ); + }, +); + +export { Theme, useThemeInternal }; diff --git a/@navikt/core/react/src/theme/index.ts b/@navikt/core/react/src/theme/index.ts new file mode 100644 index 0000000000..3178633c2c --- /dev/null +++ b/@navikt/core/react/src/theme/index.ts @@ -0,0 +1,2 @@ +"use client"; +export { Theme } from "./Theme"; diff --git a/@navikt/core/react/src/timeline/Pin.tsx b/@navikt/core/react/src/timeline/Pin.tsx index 842e2bf009..59899fe6da 100644 --- a/@navikt/core/react/src/timeline/Pin.tsx +++ b/@navikt/core/react/src/timeline/Pin.tsx @@ -15,7 +15,7 @@ import { } from "@floating-ui/react"; import { format } from "date-fns"; import React, { forwardRef, useRef, useState } from "react"; -import { UNSAFE_useAkselTheme } from "../provider"; +import { useThemeInternal } from "../theme/Theme"; import { useMergeRefs } from "../util/hooks/useMergeRefs"; import { useI18n } from "../util/i18n/i18n.context"; import { useTimelineContext } from "./hooks/useTimelineContext"; @@ -48,7 +48,7 @@ export const Pin = forwardRef( const arrowRef = useRef(null); const translate = useI18n("Timeline"); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const showArrow = !themeContext; const { diff --git a/@navikt/core/react/src/timeline/period/ClickablePeriod.tsx b/@navikt/core/react/src/timeline/period/ClickablePeriod.tsx index 288d8e555d..8285ddfbeb 100644 --- a/@navikt/core/react/src/timeline/period/ClickablePeriod.tsx +++ b/@navikt/core/react/src/timeline/period/ClickablePeriod.tsx @@ -15,7 +15,7 @@ import { } from "@floating-ui/react"; import cl from "clsx"; import React, { useRef, useState } from "react"; -import { UNSAFE_useAkselTheme } from "../../provider"; +import { useThemeInternal } from "../../theme/Theme"; import { useMergeRefs } from "../../util/hooks/useMergeRefs"; import { useI18n } from "../../util/i18n/i18n.context"; import { usePeriodContext } from "../hooks/usePeriodContext"; @@ -56,7 +56,7 @@ const ClickablePeriod = React.memo( const arrowRef = useRef(null); const translate = useI18n("Timeline"); - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const showArrow = !themeContext; const { diff --git a/@navikt/core/react/src/tooltip/Tooltip.tsx b/@navikt/core/react/src/tooltip/Tooltip.tsx index aebb885b3b..de8984d65d 100644 --- a/@navikt/core/react/src/tooltip/Tooltip.tsx +++ b/@navikt/core/react/src/tooltip/Tooltip.tsx @@ -15,8 +15,8 @@ import cl from "clsx"; import React, { HTMLAttributes, forwardRef, useRef } from "react"; import { useModalContext } from "../modal/Modal.context"; import { Portal } from "../portal"; -import { UNSAFE_useAkselTheme } from "../provider"; import { Slot } from "../slot/Slot"; +import { useThemeInternal } from "../theme/Theme"; import { Detail } from "../typography"; import { useId } from "../util/hooks"; import { useControllableState } from "../util/hooks/useControllableState"; @@ -124,7 +124,7 @@ export const Tooltip = forwardRef( }, ref, ) => { - const themeContext = UNSAFE_useAkselTheme(false); + const themeContext = useThemeInternal(false); const showArrow = _arrow && !themeContext; const [_open, _setOpen] = useControllableState({