diff --git a/.changeset/remove-styled-components.md b/.changeset/remove-styled-components.md new file mode 100644 index 000000000..50a08841c --- /dev/null +++ b/.changeset/remove-styled-components.md @@ -0,0 +1,11 @@ +--- +'@clickhouse/click-ui': major +--- + +Remove styled-components as a dependency. click-ui no longer uses styled-components internally — every component renders from CSS Modules, and the runtime theme is now delivered through click-ui's own React context (via `ClickUIProvider`). + +Breaking changes: + +- `styled-components` is no longer a peer dependency. If your app doesn't use styled-components for its own code, you can remove it. +- The deprecated `linkStyles` and `StyledLinkProps` exports have been removed. Use the `Link` component with the `component` prop instead — e.g. `text`. +- The lower-level `ThemeProvider` export has been removed; `ClickUIProvider` is now the single provider. It already set the theme (and wraps toast/tooltip), so replace any bare `` with ``. diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 07a324dbb..27de30ff3 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,24 +1,6 @@ -import { useState, useEffect, ReactNode } from "react"; import type { Preview } from "@storybook/react-vite"; -import { Decorator } from "@storybook/react-vite"; -import { styled } from "styled-components"; import { themes } from "storybook/theming"; -import { ClickUIProvider } from "../src/providers"; - -const ThemeBlock = styled.div<{ $left?: boolean; $bfill?: boolean }>( - ({ $left, $bfill: fill, theme }) => ` - position: absolute; - top: 0.5rem; - left: ${$left || fill ? 0 : "50vw"}; - right: 0; - height: fit-content; - bottom: 0; - overflow: auto; - padding: 1rem; - box-sizing: border-box; - background: ${theme.click.storybook.global.background}; - ` -); +import { withTheme } from "./withThemeDecorator"; export const globalTypes = { theme: { @@ -37,55 +19,6 @@ export const globalTypes = { }, }; -const getSystemTheme = (): "dark" | "light" => { - if (typeof window !== "undefined" && window.matchMedia) { - return window.matchMedia("(prefers-color-scheme: dark)").matches - ? "dark" - : "light"; - } - return "dark"; -}; - -interface ThemeWrapperProps { - themeSelection: string | undefined; - children: ReactNode; -} - -const ThemeWrapper = ({ themeSelection, children }: ThemeWrapperProps) => { - const [systemTheme, setSystemTheme] = useState<"dark" | "light">(getSystemTheme); - - // Listen for system theme changes - useEffect(() => { - const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); - const handleChange = () => { - setSystemTheme(mediaQuery.matches ? "dark" : "light"); - }; - mediaQuery.addEventListener("change", handleChange); - return () => mediaQuery.removeEventListener("change", handleChange); - }, []); - - // Resolve the actual theme: handle "system" and fallback for undefined/null - const theme = - themeSelection === "system" || !themeSelection ? systemTheme : themeSelection; - - return ( - - {children} - - ); -}; - -const withTheme: Decorator = (StoryFn, context) => { - const parameters = context.parameters; - const themeSelection = parameters?.theme || context.globals.theme; - - return ( - - - - ); -}; - const preview: Preview = { parameters: { options: { diff --git a/.storybook/withThemeDecorator.tsx b/.storybook/withThemeDecorator.tsx new file mode 100644 index 000000000..0305a298e --- /dev/null +++ b/.storybook/withThemeDecorator.tsx @@ -0,0 +1,83 @@ +import { useState, useEffect, ReactNode } from "react"; +import { Decorator } from "@storybook/react-vite"; +import { ClickUIProvider } from "../src/providers"; +import { useTheme } from "../src/theme/ThemeContext"; + +const ThemeBlock = ({ + left, + bfill, + children, +}: { + left?: boolean; + bfill?: boolean; + children: ReactNode; +}) => { + const theme = useTheme(); + return ( +
+ {children} +
+ ); +}; + +const getSystemTheme = (): "dark" | "light" => { + if (typeof window !== "undefined" && window.matchMedia) { + return window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + } + return "dark"; +}; + +interface ThemeWrapperProps { + themeSelection: string | undefined; + children: ReactNode; +} + +const ThemeWrapper = ({ themeSelection, children }: ThemeWrapperProps) => { + const [systemTheme, setSystemTheme] = useState<"dark" | "light">(getSystemTheme); + + // Listen for system theme changes + useEffect(() => { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + const handleChange = () => { + setSystemTheme(mediaQuery.matches ? "dark" : "light"); + }; + mediaQuery.addEventListener("change", handleChange); + return () => mediaQuery.removeEventListener("change", handleChange); + }, []); + + // Resolve the actual theme: handle "system" and fallback for undefined/null + const theme = + themeSelection === "system" || !themeSelection ? systemTheme : themeSelection; + + return ( + + {children} + + ); +}; + +export const withTheme: Decorator = (StoryFn, context) => { + const parameters = context.parameters; + const themeSelection = parameters?.theme || context.globals.theme; + + return ( + + + + ); +}; diff --git a/package.json b/package.json index 8febea6f0..93e34284b 100644 --- a/package.json +++ b/package.json @@ -424,8 +424,7 @@ "react-syntax-highlighter": "^16.1.0", "react-virtualized-auto-sizer": "^1.0.20", "react-window": "^1.8.9", - "sortablejs": "^1.15.0", - "styled-components": "^6.1.11" + "sortablejs": "^1.15.0" }, "devDependencies": { "@changesets/cli": "^2.29.8", @@ -450,7 +449,6 @@ "@types/react-window": "^1.8.8", "@types/sortablejs": "^1.15.2", "@vitejs/plugin-react": "^5.1.2", - "babel-plugin-styled-components": "^2.1.4", "chromatic": "^13.3.4", "eslint": "^9", "eslint-import-resolver-typescript": "^4.4.4", @@ -492,13 +490,11 @@ "peerDependencies": { "dayjs": "^1.11.19", "react": "^18.3.1 || ^19.0.0", - "react-dom": "^18.3.1 || ^19.0.0", - "styled-components": "^6.1.11" + "react-dom": "^18.3.1 || ^19.0.0" }, "resolutions": { "@types/react": "18.3.1", - "@types/react-dom": "18.3.1", - "styled-components": "6.1.11" + "@types/react-dom": "18.3.1" }, "packageManager": "yarn@4.5.3", "engines": { diff --git a/src/components/Assets/Flags/system/Flag.tsx b/src/components/Assets/Flags/system/Flag.tsx index 0d8556762..109858357 100644 --- a/src/components/Assets/Flags/system/Flag.tsx +++ b/src/components/Assets/Flags/system/Flag.tsx @@ -1,5 +1,5 @@ import { SVGAttributes } from 'react'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import { getFallbackThemeName } from '@/theme/theme.utils'; import { SvgImageElement } from '@/components/Icon/SvgImageElement'; import { FlagName, FlagProps } from './types'; diff --git a/src/components/Assets/Icons/system/Icon.tsx b/src/components/Assets/Icons/system/Icon.tsx index 42bc8ede7..999ca4fa2 100644 --- a/src/components/Assets/Icons/system/Icon.tsx +++ b/src/components/Assets/Icons/system/Icon.tsx @@ -1,5 +1,5 @@ import { SVGAttributes } from 'react'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import { getFallbackThemeName } from '@/theme/theme.utils'; import { SvgImageElement } from '@/components/Icon/SvgImageElement'; import { IconName, IconProps } from './types'; diff --git a/src/components/Assets/Logos/system/Logo.tsx b/src/components/Assets/Logos/system/Logo.tsx index 49b937550..a054813c2 100644 --- a/src/components/Assets/Logos/system/Logo.tsx +++ b/src/components/Assets/Logos/system/Logo.tsx @@ -1,5 +1,5 @@ import { SVGAttributes } from 'react'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import { getFallbackThemeName } from '@/theme/theme.utils'; import { SvgImageElement } from '@/components/Icon/SvgImageElement'; import { LogoName, LogoProps } from './types'; diff --git a/src/components/Assets/Payments/system/Payment.tsx b/src/components/Assets/Payments/system/Payment.tsx index 291ee2a83..9f38a5cf5 100644 --- a/src/components/Assets/Payments/system/Payment.tsx +++ b/src/components/Assets/Payments/system/Payment.tsx @@ -1,5 +1,5 @@ import { SVGAttributes } from 'react'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import { getFallbackThemeName } from '@/theme/theme.utils'; import { SvgImageElement } from '@/components/Icon/SvgImageElement'; import { PaymentName, PaymentProps } from './types'; diff --git a/src/components/Badge/Badge.module.css b/src/components/Badge/Badge.module.css index c90a072cd..b16ae0849 100644 --- a/src/components/Badge/Badge.module.css +++ b/src/components/Badge/Badge.module.css @@ -108,9 +108,9 @@ font: var(--click-badge-typography-label-md-default); } -/* Content row — mirrors the inner Content styled component. The original - `Content` was never passed `$size`, so its `gap` always defaulted to the - md token regardless of the badge's actual `size`. Preserved verbatim. */ +/* Content row `gap` always uses the md token regardless of the badge's actual + `size` — matching the pre-migration behavior, where the row was never passed + a size. Preserved verbatim. */ .content { display: inline-flex; @@ -120,28 +120,25 @@ gap: var(--click-badge-space-md-gap); } -/* BadgeContent — wraps the IconWrapper. The original styled(IconWrapper) set - `width: auto; overflow: hidden;` on the IconWrapper's rendered root and - styled the descendant with color, height, width, and `gap: inherit`. +/* BadgeContent — wraps the IconWrapper. Sets `width: auto; overflow: hidden;` + on the IconWrapper's root and styles the descendant with color, + height, width, and `gap: inherit`. - The styled-components version only forwarded `$state` to BadgeContent; - `$type` and `$size` were NOT passed and defaulted to `'opaque'` and `'md'` - inside the styled rule. To preserve that behavior verbatim, the descendant - rules below key off `state` only — color always comes from the - opaque-text-* tokens, and dimensions are always md, regardless of the - badge's actual `type`/`size`. + The content wrapper is keyed off `state` only — `type` and `size` are not + applied and effectively default to `'opaque'` and `'md'`, matching the + pre-migration behavior. So the descendant color always comes from the + opaque-text-* tokens and dimensions are always md, regardless of the badge's + actual `type`/`size`. The doubled-class selectors below (e.g. `.badgecontent.badgecontent svg`) raise specificity to (0,2,1) so these rules win over the Icon component's - own runtime-injected styled-components rule `. svg { width: ... }` - (specificity 0,1,1). styled-components used to inject the BadgeContent - stylesheet AFTER Icon's stylesheet at render time, so source order let - BadgeContent win at equal specificity; CSS Modules now bundle Badge's - stylesheet earlier than the runtime SC stylesheet, so we need the bump. */ + own `svg` rule `. svg { width: ... }` (specificity 0,1,1). Badge + and Icon are both CSS Modules in the same cascade layer, and Badge's rule + bundles earlier, so at equal specificity Icon would win on source order — + the bump keeps BadgeContent's sizing authoritative. */ /* stylelint-disable no-descending-specificity -- doubled-class selectors below - raise specificity so they win over the Icon component's runtime-injected - styled-components `& svg` rule. */ + raise specificity so they win over the Icon component's `& svg` rule. */ .badgecontent { width: auto; @@ -184,19 +181,17 @@ /* stylelint-enable no-descending-specificity */ -/* Close icon — mirrors the SvgContainer styled.svg with as={Icon}. The - styled-components version only passed $state (size and type defaulted), - so width/height come from the default md/opaque tokens, and color from - the opaque-text- token. +/* Close icon — rendered as an Icon, keyed off `state` only (size and type + default), so width/height come from the default md/opaque tokens and color + from the opaque-text- token. The state classes below use doubled-class specificity (0,2,1) so they - always win over Icon's internal `SvgWrapper` styled-component rule that - sets `color: currentColor` on the same element at single-class specificity. - Without the bump, stylesheet-order would decide and CSS Modules bundle - before runtime SC styles, so the close icon would inherit the wrapper's - color (e.g. opaque-text-info for an info badge, but solid-text-info for - a solid info badge). Doubled class keeps the close icon on opaque-text-* - regardless. */ + always win over Icon's own `svg` rule that sets `color: currentColor` on + the same element at single-class specificity. Without the bump, source + order would decide — Badge and Icon are CSS Modules in the same layer and + Badge bundles first — so the close icon would inherit the wrapper's color + (e.g. opaque-text-info for an info badge, but solid-text-info for a solid + info badge). Doubled class keeps the close icon on opaque-text-* regardless. */ .closeicon { width: var(--click-badge-icon-md-size-width); diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx index a1fdf0325..767b85ac5 100644 --- a/src/components/Badge/Badge.tsx +++ b/src/components/Badge/Badge.tsx @@ -33,12 +33,11 @@ const wrapperVariants = cva(styles.wrapper, { }, }); -// The original `BadgeContent = styled(IconWrapper)` only forwarded `$state` — -// `$type` and `$size` were NOT passed and defaulted to `'opaque'` and `'md'` -// inside the styled rule. So the descendant color always came from the -// opaque-text-* tokens (regardless of the badge's actual `type`) and the -// SVG dimensions were always md (regardless of the badge's actual `size`). -// To preserve byte-for-byte behavior, this variant set keys off `state` only. +// This variant set intentionally keys off `state` only: the descendant +// color always comes from the opaque-text-* tokens (regardless of the badge's +// `type`) and the SVG dimensions are always md (regardless of its `size`). +// This matches the pre-migration behavior, where the content wrapper never +// received `type`/`size` and defaulted to `'opaque'`/`'md'`. const badgeContentVariants = cva(styles.badgecontent, { variants: { state: { diff --git a/src/components/CodeBlock/CodeBlock.stories.tsx b/src/components/CodeBlock/CodeBlock.stories.tsx index 294ad3705..aa41f1c6f 100644 --- a/src/components/CodeBlock/CodeBlock.stories.tsx +++ b/src/components/CodeBlock/CodeBlock.stories.tsx @@ -22,10 +22,8 @@ const Decorator: NonNullable = [ ), ]; -// Shows both the copy button and the wrap button. Both are rendered via -// `` (CodeButton = styled(EmptyButton)), exercising -// the `as`-prop path that bypasses EmptyButton's own render. This guards that -// path against the EmptyButton CSS Modules migration. +// Shows both the copy button and the wrap button, each rendered via IconButton. +// Keeps both buttons in the visual-regression suite. export const WithWrapButton: Story = { args: { children: 'SELECT customer_id, total_spent FROM orders LIMIT 10;', diff --git a/src/components/CodeBlock/useColorStyle.ts b/src/components/CodeBlock/useColorStyle.ts index f5e1ca22d..87e52c511 100644 --- a/src/components/CodeBlock/useColorStyle.ts +++ b/src/components/CodeBlock/useColorStyle.ts @@ -1,5 +1,5 @@ import type { CSSProperties } from 'react'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import { CodeThemeType } from './CodeBlock.types'; const useColorStyle = (defaultTheme?: CodeThemeType): Record => { diff --git a/src/components/ContextMenu/ContextMenu.stories.tsx b/src/components/ContextMenu/ContextMenu.stories.tsx index 7e053c5ab..9d3eba3d6 100644 --- a/src/components/ContextMenu/ContextMenu.stories.tsx +++ b/src/components/ContextMenu/ContextMenu.stories.tsx @@ -2,23 +2,23 @@ import React from 'react'; import { Meta, StoryObj } from '@storybook/react-vite'; import { ContextMenuProps } from '@radix-ui/react-context-menu'; import { ContextMenu, ContextMenuItemProps } from '@/components/ContextMenu'; -import { styled } from 'styled-components'; interface ContextMenuExampleProps extends ContextMenuProps { disabled?: boolean; showArrow?: boolean; } -const GridCenter = styled.div` - display: grid; - place-items: center; - width: 100%; - height: 100%; -`; +const gridCenterStyle: React.CSSProperties = { + display: 'grid', + placeItems: 'center', + width: '100%', + height: '100%', +}; -const Trigger = styled(GridCenter)` - border: 2px currentColor dashed; -`; +const triggerStyle: React.CSSProperties = { + ...gridCenterStyle, + border: '2px currentColor dashed', +}; const ContextMenuExample = ({ showArrow, @@ -26,10 +26,10 @@ const ContextMenuExample = ({ ...props }: ContextMenuExampleProps) => { return ( - +
- ContextMenu Trigger +
ContextMenu Trigger
@@ -61,7 +61,7 @@ const ContextMenuExample = ({ Delete content
- +
); }; @@ -95,13 +95,13 @@ export const Playground: Story = { // Visual-regression story: a plain trigger the spec right-clicks to open the // context menu, so the menu panel + item extension styles can be screenshotted. -const VRTrigger = styled.div` - display: grid; - place-items: center; - width: 240px; - height: 120px; - border: 2px currentColor dashed; -`; +const vrTriggerStyle: React.CSSProperties = { + display: 'grid', + placeItems: 'center', + width: '240px', + height: '120px', + border: '2px currentColor dashed', +}; export const OpenContent: Story = { render: () => ( @@ -111,7 +111,7 @@ export const OpenContent: Story = { > - Right-click here +
Right-click here
) => ( +
+); const DemoPanel = (props: ComponentProps>) => ( { - return render({component}); + return render({component}); }; describe('GenericMenu', () => { diff --git a/src/components/GridCenter/GridCenter.tsx b/src/components/GridCenter/GridCenter.tsx index e25ddfaf6..a5d762b2d 100644 --- a/src/components/GridCenter/GridCenter.tsx +++ b/src/components/GridCenter/GridCenter.tsx @@ -9,7 +9,7 @@ import { cn } from '@/lib/cva'; import styles from './GridCenter.module.css'; interface GridCenterOwnProps { - /** Render as a different element/component (mirrors styled-components `as`). */ + /** Render as a different element/component. */ as?: ElementType; } diff --git a/src/components/GridContainer/GridContainer.stories.tsx b/src/components/GridContainer/GridContainer.stories.tsx index 4a47ae7bf..452716ddc 100644 --- a/src/components/GridContainer/GridContainer.stories.tsx +++ b/src/components/GridContainer/GridContainer.stories.tsx @@ -1,14 +1,14 @@ import { Meta, StoryObj } from '@storybook/react-vite'; import { GridContainer } from '@/components/GridContainer'; import { Text } from '@/components/Text'; -import { styled } from 'styled-components'; +import type { CSSProperties } from 'react'; -const GridCenter = styled.div` - display: grid; - justify-items: center; - width: 100%; - height: 120px; -`; +const gridCenterStyle: CSSProperties = { + display: 'grid', + justifyItems: 'center', + width: '100%', + height: '120px', +}; const meta: Meta = { component: GridContainer, @@ -49,7 +49,7 @@ export const Playground: Story = { rowGap: 'none', }, render: args => ( - +
Parent container - +
), }; diff --git a/src/components/InputWrapper/InputWrapper.tsx b/src/components/InputWrapper/InputWrapper.tsx index 071f11de6..847ab9105 100644 --- a/src/components/InputWrapper/InputWrapper.tsx +++ b/src/components/InputWrapper/InputWrapper.tsx @@ -112,7 +112,7 @@ const inputVariants = cva(styles.input, { interface InputElementOwnProps { $hasStartContent?: boolean; $hasEndContent?: boolean; - /** Render as a different element/component (mirrors styled-components `as`). */ + /** Render as a different element/component. */ as?: ElementType; } diff --git a/src/components/Label/Label.module.css b/src/components/Label/Label.module.css index 0d88ad649..4d7c256ac 100644 --- a/src/components/Label/Label.module.css +++ b/src/components/Label/Label.module.css @@ -4,12 +4,11 @@ } /* Hover/focus rules only apply when neither disabled nor error is set, matching - the original styled-components behavior (which simply didn't emit them in - those states). The :not() chain also raises specificity above 0-1-0 so these - rules win over InputWrapper's `labelColor` override (a 0-1-0 single-class - styled-components selector injected later in the cascade). The .label_error - and .label_disabled modifiers below stay at 0-1-0 on purpose, so labelColor - continues to override them. */ + the pre-migration behavior (which simply didn't emit them in those states). + The :not() chain also raises specificity above 0-1-0 so these rules win over + InputWrapper's `labelColor` override. The .label_error and .label_disabled + modifiers below stay at 0-1-0 on purpose, so labelColor continues to + override them. */ .label:not(.label_error, .label_disabled):hover { color: var(--click-field-color-label-hover); font: var(--click-field-typography-label-hover); diff --git a/src/components/SidebarNavigationItem/SidebarNavigationItem.module.css b/src/components/SidebarNavigationItem/SidebarNavigationItem.module.css index ebbc26e63..cb4208b34 100644 --- a/src/components/SidebarNavigationItem/SidebarNavigationItem.module.css +++ b/src/components/SidebarNavigationItem/SidebarNavigationItem.module.css @@ -1,7 +1,7 @@ /* stylelint-disable custom-property-pattern -- design tokens use camelCase (e.g. sqlSidebar) */ .wrapper { display: flex; - /* stylelint-disable plugin/no-unsupported-browser-features, declaration-property-value-no-unknown -- the original styled rule used this + /* stylelint-disable plugin/no-unsupported-browser-features, declaration-property-value-no-unknown -- the pre-migration styling used this same width fallback chain (100% → -webkit-fill-available → fill-available → stretch) so the item fills the available inline space. */ width: 100%; @@ -67,7 +67,7 @@ cursor: not-allowed; } -/* stylelint-disable-next-line media-feature-range-notation -- prefix syntax matches the original styled rule and is more broadly supported than range syntax */ +/* stylelint-disable-next-line media-feature-range-notation -- prefix syntax matches the pre-migration styling and is more broadly supported than range syntax */ @media (max-width: 640px) { .wrapper { padding: var(--item-mobile-space-y) var(--item-mobile-space-right) diff --git a/src/components/SidebarNavigationTitle/SidebarNavigationTitle.module.css b/src/components/SidebarNavigationTitle/SidebarNavigationTitle.module.css index e5ed68638..2b4839c46 100644 --- a/src/components/SidebarNavigationTitle/SidebarNavigationTitle.module.css +++ b/src/components/SidebarNavigationTitle/SidebarNavigationTitle.module.css @@ -1,6 +1,6 @@ /* stylelint-disable custom-property-pattern -- design tokens use camelCase (e.g. sqlSidebar) */ .wrapper { - /* stylelint-disable plugin/no-unsupported-browser-features, declaration-property-value-no-unknown -- the original styled rule used this + /* stylelint-disable plugin/no-unsupported-browser-features, declaration-property-value-no-unknown -- the pre-migration styling used this same width fallback chain (100% → -webkit-fill-available → fill-available → stretch) so the title fills the available inline space. */ width: 100%; @@ -25,8 +25,8 @@ wrapper `as={Collapsible.Trigger}`, so the element also carries `.collapsible__trigger`, whose `display/align-items/color/font/cursor` resets would otherwise win on CSS-module bundle order and strip the title's - own typography. The original styled-components cascade resolved in the - title's favour; the doubled class makes that deterministic. Only the + own typography. The pre-migration cascade resolved in the title's favour; + the doubled class makes that deterministic. Only the properties that actually conflict with the trigger live here — box metrics and the `--title-*` custom properties stay single-class above/below so the `type`/`collapsible` modifiers still override them. */ diff --git a/src/components/Text/Text.module.css b/src/components/Text/Text.module.css index 320d2686a..e939b550e 100644 --- a/src/components/Text/Text.module.css +++ b/src/components/Text/Text.module.css @@ -10,7 +10,7 @@ } /* font shorthand depends on both size and weight; one class per pairing - mirrors the single `font:` declaration emitted by styled-components. */ + emits a single `font:` declaration. */ .text_font_normal-xs { font: var(--typography-styles-product-text-normal-xs); diff --git a/src/components/TextField/TextField.test.tsx b/src/components/TextField/TextField.test.tsx index 31f1d5140..e12b3ea47 100644 --- a/src/components/TextField/TextField.test.tsx +++ b/src/components/TextField/TextField.test.tsx @@ -43,9 +43,9 @@ describe('TextField', () => { // `.label { color: var(--click-field-color-label-default) }`, which // jsdom does not load. Visual parity is covered by the Playwright // suite at tests/display/label.spec.ts. The assertion below verifies - // the InputWrapper does not inject a styled-components color override - // when no labelColor prop is passed (element.style.color is the empty - // string when no inline `color` is set). + // the InputWrapper does not inject an inline color override when no + // labelColor prop is passed (element.style.color is the empty string + // when no inline `color` is set). expect(labelElement.style.color).toBe(''); }); diff --git a/src/components/Title/Title.module.css b/src/components/Title/Title.module.css index d8e341db9..b01c5170c 100644 --- a/src/components/Title/Title.module.css +++ b/src/components/Title/Title.module.css @@ -22,7 +22,7 @@ } /* font shorthand depends on both family and size; one class per pairing - mirrors the single `font:` declaration emitted by styled-components. */ + emits a single `font:` declaration. */ .title_font_product-xs { font: var(--typography-styles-product-titles-xs); diff --git a/src/hooks/useCUITheme.ts b/src/hooks/useCUITheme.ts index e6a1c538f..649d5cfca 100644 --- a/src/hooks/useCUITheme.ts +++ b/src/hooks/useCUITheme.ts @@ -1,6 +1,6 @@ 'use client'; -import { useTheme } from 'styled-components'; +import { useTheme } from '@/theme/ThemeContext'; import type { Theme } from '@/theme/theme.types'; export type CUIThemeType = Pick; diff --git a/src/hooks/useInitialTheme.ts b/src/hooks/useInitialTheme.ts index a012c84ff..ba96c6700 100644 --- a/src/hooks/useInitialTheme.ts +++ b/src/hooks/useInitialTheme.ts @@ -37,7 +37,9 @@ export const useInitialTheme = ({ return defaultTheme; }); - // TODO: Remove mounted once migrated from styled-components + // `mounted` gates against SSR/hydration mismatches: it flips true only after + // the client-side effect runs, letting consumers defer theme-dependent output + // until the stored theme has been reconciled. const [mounted, setMounted] = useState(false); useEffect(() => { diff --git a/src/index.ts b/src/index.ts index b2298009f..569c5f27d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -290,12 +290,9 @@ export type { // Providers // ================================================ -// TODO: Having both ClickUIProvider and ThemeProvider exported is confusing. -// Consider consolidating to a single public provider API. For example, have ThemeProvider only! export { ClickUIProvider, PortalProvider, - ThemeProvider, usePortalContainer, useResolvedPortalContainer, } from './providers'; @@ -356,22 +353,3 @@ export type { HoverCardProps } from '@radix-ui/react-hover-card'; * Consider using click-ui's Popover component API instead. */ export type { PopoverProps } from '@radix-ui/react-popover'; - -// ================================================ -// Deprecated Exports -// These exports are deprecated and will be removed in a future version. -// They are kept here temporarily for backward compatibility. -// ================================================ - -/** - * @deprecated Use the `Link` component with the `component` prop instead. - * Example: `text` - */ -export { linkStyles } from './styles/linkStyles'; - -/** - * @deprecated Use the `Link` component with the `component` prop instead. - * This type exposes internal styled-components implementation details. - * Example: `text` - */ -export type { StyledLinkProps } from './styles/linkStyles'; diff --git a/src/providers/ClickUIProvider.tsx b/src/providers/ClickUIProvider.tsx index b38605324..96180c5f2 100644 --- a/src/providers/ClickUIProvider.tsx +++ b/src/providers/ClickUIProvider.tsx @@ -1,16 +1,22 @@ +'use client'; + import { Provider as TooltipProvider, TooltipProviderProps, } from '@radix-ui/react-tooltip'; import { ToastProvider, ToastProviderProps } from '@/components/Toast/Toast'; -import { THEMES } from '@/theme/theme.core'; +import { THEMES, themes } from '@/theme/theme.core'; import type { ThemeName } from '@/theme/theme.types'; -import { ThemeProvider } from './ThemeProvider'; +import { ThemeContext } from '@/theme/ThemeContext'; import { ReactNode, useEffect } from 'react'; import { setRootThemeAttribute, removeRootThemeAttribute } from '@/utils/dom'; import { CUI_THEME_STORAGE_KEY } from '@/utils/localStorage'; import { isValidThemeName, getFallbackThemeName } from '@/theme/theme.utils'; +import '@/theme/styles/tokens-light.css'; +import '@/theme/styles/tokens-dark.css'; +import '@/theme/styles/global.css'; + interface Props { config?: { tooltip?: Omit; @@ -62,10 +68,10 @@ export const ClickUIProvider = ({ }, [safeTheme, persistTheme, storageKey]); return ( - + {children} - + ); }; diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx deleted file mode 100644 index 7edad26f3..000000000 --- a/src/providers/ThemeProvider.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { - ThemeProvider as StyledThemeProvider, - createGlobalStyle, -} from 'styled-components'; -import { THEMES } from '@/theme/theme.core'; -import type { ThemeName } from '@/theme/theme.types'; -import { themes } from '@/theme/theme.core'; -import { isValidThemeName } from '@/theme/theme.utils'; - -import '@/theme/styles/tokens-light.css'; -import '@/theme/styles/tokens-dark.css'; - -const GlobalStyle = createGlobalStyle` - body{ - color: ${props => props.theme.click.global.color.text.default}; - background-color: ${props => props.theme.click.global.color.background.default} - } -`; - -export const ThemeProvider = ({ - theme: name, - children, -}: { - theme: ThemeName; - children: React.ReactNode; -}) => { - const hasValidTheme = isValidThemeName(name); - const resolvedTheme = !hasValidTheme ? THEMES.Light : name; - return ( - - - {children} - - ); -}; diff --git a/src/providers/index.ts b/src/providers/index.ts index 4ace8ff77..df48a07ca 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -1,11 +1,9 @@ -// TODO: Reconcile ClickUIProvider and ThemeProvider - having two public providers is confusing. -// Only one should be public. ClickUIProvider is the main composite provider that includes -// theme, toast, and tooltip providers. ThemeProvider is the lower-level styled-components -// theme provider. Consider deprecating one or making ThemeProvider internal only. +// ClickUIProvider is the single public provider: it sets the theme (data-cui-theme +// + the theme React context), persists it, and wraps the toast and tooltip +// providers. The active theme is read with `useTheme` from '@/theme/ThemeContext'. export { ClickUIProvider } from './ClickUIProvider'; export { PortalProvider } from './PortalProvider'; export { usePortalContainer, useResolvedPortalContainer } from './PortalContext'; export type { PortalContainer } from './PortalContext'; export type { PortalProviderProps } from './PortalProvider'; -export { ThemeProvider } from './ThemeProvider'; diff --git a/src/stories/chartColors.stories.tsx b/src/stories/chartColors.stories.tsx index 5f7b96e8b..8c9ffbc5e 100644 --- a/src/stories/chartColors.stories.tsx +++ b/src/stories/chartColors.stories.tsx @@ -1,14 +1,7 @@ import { Container } from '@/components/Container'; import { Text } from '@/components/Text'; -import { styled, useTheme } from 'styled-components'; - -const ColorBox = styled(Container)<{ $color: string }>` - ${({ $color }) => ` - background-color: ${$color}; - `} - border-radius: 4px; -`; +import { useTheme } from '@/theme/ThemeContext'; const ChartColorsDemo = () => { const theme = useTheme(); @@ -27,10 +20,13 @@ const ChartColorsDemo = () => { key={name} > {name} - {hex} diff --git a/src/styles/linkStyles.ts b/src/styles/linkStyles.ts deleted file mode 100644 index 481daa3c9..000000000 --- a/src/styles/linkStyles.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { css } from 'styled-components'; -import type { TextSize, TextWeight } from '@/components/Text'; - -export type StyledLinkProps = { $size: TextSize; $weight: TextWeight }; - -export const linkStyles = css` - font: ${({ $size, $weight = 'normal', theme }) => - theme.typography.styles.product.text[$weight][$size]}; - color: ${({ theme }) => theme.click.global.color.text.link.default}; - margin: 0; - text-decoration: none; - display: inline-flex; - gap: ${({ $size, theme }) => - $size === 'xs' || $size === 'sm' - ? theme.click.link.space.sm.gap - : theme.click.link.space.md.gap}; - margin-right: ${({ $size, theme }) => - $size === 'xs' || $size === 'sm' - ? theme.click.link.space.sm.gap - : theme.click.link.space.md.gap}; - align-items: center; - - &:hover, - &:focus { - color: ${({ theme }) => theme.click.global.color.text.link.hover}; - transition: ${({ theme }) => theme.transition.default}; - text-decoration: underline; - cursor: pointer; - } - - &:visited { - color: ${({ theme }) => theme.click.global.color.text.link.default}; - } -`; diff --git a/src/theme/ThemeContext.ts b/src/theme/ThemeContext.ts new file mode 100644 index 000000000..41f67dfd3 --- /dev/null +++ b/src/theme/ThemeContext.ts @@ -0,0 +1,13 @@ +'use client'; + +import { createContext, useContext } from 'react'; +import type { Theme } from './theme.types'; +import { themes } from './theme.core'; + +export const ThemeContext = createContext(themes.light); + +/** + * Returns the active click-ui theme object. Replaces styled-components' + * `useTheme` as the runtime theme-delivery mechanism. + */ +export const useTheme = (): Theme => useContext(ThemeContext); diff --git a/src/theme/styles/global.css b/src/theme/styles/global.css new file mode 100644 index 000000000..f4df5b792 --- /dev/null +++ b/src/theme/styles/global.css @@ -0,0 +1,8 @@ +/* Page-level defaults: apply the theme's global text/background colors to + , swapping with the active `data-cui-theme`. Replaces the old + styled-components `createGlobalStyle` rule. Wrapped in the `clickui` cascade + layer (like all click-ui styles), so any consumer `body` rule overrides it. */ +body { + color: var(--click-global-color-text-default); + background-color: var(--click-global-color-background-default); +} diff --git a/src/theme/theme.types.ts b/src/theme/theme.types.ts index 90c1ef2de..ea9990360 100644 --- a/src/theme/theme.types.ts +++ b/src/theme/theme.types.ts @@ -3,7 +3,3 @@ import lightTheme from './tokens/variables.light'; export type Theme = typeof lightTheme; export type ThemeName = 'dark' | 'light'; - -declare module 'styled-components' { - export interface DefaultTheme extends Theme {} -} diff --git a/tests/codeblocks/codeblock.spec.ts b/tests/codeblocks/codeblock.spec.ts index 8fe79cbf2..fa1cfa6dd 100644 --- a/tests/codeblocks/codeblock.spec.ts +++ b/tests/codeblocks/codeblock.spec.ts @@ -1,16 +1,14 @@ // Affected-spec coverage for scoped visual-regression runs in CI. // See .scripts/js/affected-visual-specs // @covers src/components/CodeBlock -// @covers src/components/EmptyButton +// @covers src/components/IconButton import { test as it, expect } from '@playwright/test'; import { getStoryUrl } from '../utils'; const { describe, use } = it; -// The copy and wrap buttons are rendered via `` where -// CodeButton = styled(EmptyButton). The `as` prop bypasses EmptyButton's own -// render, so this spec guards that those buttons still render correctly after -// EmptyButton's CSS Modules migration. The WithWrapButton story shows both. +// The copy and wrap buttons are rendered via IconButton. This spec guards that +// those buttons still render correctly. The WithWrapButton story shows both. const rootLocator = '[data-testid="codeblock-harness"]'; const inlineLocator = '[data-testid="inline-codeblock-harness"]'; diff --git a/vite.config.ts b/vite.config.ts index 95b225f3a..f5b3f44b8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -98,19 +98,7 @@ const viteConfig = defineConfig({ }, }, plugins: [ - react({ - // TODO: On styled-components migration completion - // remove styled-components babel plugins - babel: { - plugins: [['babel-plugin-styled-components', { displayName: false }]], - - env: { - development: { - plugins: [['babel-plugin-styled-components', { displayName: true }]], - }, - }, - }, - }), + react(), dts({ outDir: 'dist/types', include: ['src/**/*'], diff --git a/yarn.lock b/yarn.lock index f67524f54..76b5d04e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -164,15 +164,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.22.5": - version: 7.27.3 - resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" - dependencies: - "@babel/types": "npm:^7.27.3" - checksum: 10c0/94996ce0a05b7229f956033e6dcd69393db2b0886d0db6aff41e704390402b8cdcca11f61449cb4f86cfd9e61b5ad3a73e4fa661eeed7846b125bd1c33dbc633 - languageName: node - linkType: hard - "@babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" @@ -206,7 +197,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.22.5, @babel/helper-module-imports@npm:^7.27.1": +"@babel/helper-module-imports@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-module-imports@npm:7.27.1" dependencies: @@ -333,17 +324,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.22.5": - version: 7.27.1 - resolution: "@babel/plugin-syntax-jsx@npm:7.27.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.27.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/bc5afe6a458d5f0492c02a54ad98c5756a0c13bd6d20609aae65acd560a9e141b0876da5f358dce34ea136f271c1016df58b461184d7ae9c4321e0f98588bc84 - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx-self@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-react-jsx-self@npm:7.27.1" @@ -447,7 +427,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6": version: 7.28.6 resolution: "@babel/types@npm:7.28.6" dependencies: @@ -824,7 +804,6 @@ __metadata: "@types/react-window": "npm:^1.8.8" "@types/sortablejs": "npm:^1.15.2" "@vitejs/plugin-react": "npm:^5.1.2" - babel-plugin-styled-components: "npm:^2.1.4" chromatic: "npm:^13.3.4" class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" @@ -857,7 +836,6 @@ __metadata: storybook: "npm:^10.1.10" storybook-addon-pseudo-states: "npm:^10.1.10" style-dictionary: "npm:^5.0.0" - styled-components: "npm:^6.1.11" stylelint: "npm:^17.5.0" stylelint-config-standard: "npm:^40.0.0" stylelint-no-unsupported-browser-features: "npm:^8.1.1" @@ -876,7 +854,6 @@ __metadata: dayjs: ^1.11.19 react: ^18.3.1 || ^19.0.0 react-dom: ^18.3.1 || ^19.0.0 - styled-components: ^6.1.11 languageName: unknown linkType: soft @@ -1029,29 +1006,6 @@ __metadata: languageName: node linkType: hard -"@emotion/is-prop-valid@npm:1.2.2": - version: 1.2.2 - resolution: "@emotion/is-prop-valid@npm:1.2.2" - dependencies: - "@emotion/memoize": "npm:^0.8.1" - checksum: 10c0/bb1530dcb4e0e5a4fabb219279f2d0bc35796baf66f6241f98b0d03db1985c890a8cafbea268e0edefd5eeda143dbd5c09a54b5fba74cee8c69b98b13194af50 - languageName: node - linkType: hard - -"@emotion/memoize@npm:^0.8.1": - version: 0.8.1 - resolution: "@emotion/memoize@npm:0.8.1" - checksum: 10c0/dffed372fc3b9fa2ba411e76af22b6bb686fb0cb07694fdfaa6dd2baeb0d5e4968c1a7caa472bfcf06a5997d5e7c7d16b90e993f9a6ffae79a2c3dbdc76dfe78 - languageName: node - linkType: hard - -"@emotion/unitless@npm:0.8.1": - version: 0.8.1 - resolution: "@emotion/unitless@npm:0.8.1" - checksum: 10c0/a1ed508628288f40bfe6dd17d431ed899c067a899fa293a13afe3aed1d70fac0412b8a215fafab0b42829360db687fecd763e5f01a64ddc4a4b58ec3112ff548 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/aix-ppc64@npm:0.21.5" @@ -4305,13 +4259,6 @@ __metadata: languageName: node linkType: hard -"@types/stylis@npm:4.2.5": - version: 4.2.5 - resolution: "@types/stylis@npm:4.2.5" - checksum: 10c0/23f5b35a3a04f6bb31a29d404fa1bc8e0035fcaff2356b4047743a057e0c37b2eba7efe14d57dd2b95b398cea3bac294d9c6cd93ed307d8c0b7f5d282224b469 - languageName: node - linkType: hard - "@types/unist@npm:*": version: 3.0.3 resolution: "@types/unist@npm:3.0.3" @@ -5470,21 +5417,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-styled-components@npm:^2.1.4": - version: 2.1.4 - resolution: "babel-plugin-styled-components@npm:2.1.4" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.22.5" - "@babel/helper-module-imports": "npm:^7.22.5" - "@babel/plugin-syntax-jsx": "npm:^7.22.5" - lodash: "npm:^4.17.21" - picomatch: "npm:^2.3.1" - peerDependencies: - styled-components: ">= 2" - checksum: 10c0/553f35f5feb4b51fda9c9aeef8a31c1b66f430687ab17830b7cdacfe7e93f912aef55bf59e402f4e0a1fa7ad039768ab3626512bbb9bf1f76fcc67ba47e7a56e - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -5725,13 +5657,6 @@ __metadata: languageName: node linkType: hard -"camelize@npm:^1.0.0": - version: 1.0.1 - resolution: "camelize@npm:1.0.1" - checksum: 10c0/4c9ac55efd356d37ac483bad3093758236ab686192751d1c9daa43188cc5a07b09bd431eb7458a4efd9ca22424bba23253e7b353feb35d7c749ba040de2385fb - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001759": version: 1.0.30001764 resolution: "caniuse-lite@npm:1.0.30001764" @@ -6108,13 +6033,6 @@ __metadata: languageName: node linkType: hard -"css-color-keywords@npm:^1.0.0": - version: 1.0.0 - resolution: "css-color-keywords@npm:1.0.0" - checksum: 10c0/af205a86c68e0051846ed91eb3e30b4517e1904aac040013ff1d742019b3f9369ba5658ba40901dbbc121186fc4bf0e75a814321cc3e3182fbb2feb81c6d9cb7 - languageName: node - linkType: hard - "css-functions-list@npm:^3.3.3": version: 3.3.3 resolution: "css-functions-list@npm:3.3.3" @@ -6135,17 +6053,6 @@ __metadata: languageName: node linkType: hard -"css-to-react-native@npm:3.2.0": - version: 3.2.0 - resolution: "css-to-react-native@npm:3.2.0" - dependencies: - camelize: "npm:^1.0.0" - css-color-keywords: "npm:^1.0.0" - postcss-value-parser: "npm:^4.0.2" - checksum: 10c0/fde850a511d5d3d7c55a1e9b8ed26b69a8ad4868b3487e36ebfbfc0b96fc34bc977d9cd1d61a289d0c74d3f9a662d8cee297da53d4433bf2e27d6acdff8e1003 - languageName: node - linkType: hard - "css-tokenize@npm:^1.0.1": version: 1.0.1 resolution: "css-tokenize@npm:1.0.1" @@ -6228,13 +6135,6 @@ __metadata: languageName: node linkType: hard -"csstype@npm:3.1.3": - version: 3.1.3 - resolution: "csstype@npm:3.1.3" - checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 - languageName: node - linkType: hard - "csstype@npm:^3.0.2": version: 3.2.3 resolution: "csstype@npm:3.2.3" @@ -9663,7 +9563,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.11, nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.11": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -10389,24 +10289,13 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 languageName: node linkType: hard -"postcss@npm:8.4.38": - version: 8.4.38 - resolution: "postcss@npm:8.4.38" - dependencies: - nanoid: "npm:^3.3.7" - picocolors: "npm:^1.0.0" - source-map-js: "npm:^1.2.0" - checksum: 10c0/955407b8f70cf0c14acf35dab3615899a2a60a26718a63c848cf3c29f2467b0533991b985a2b994430d890bd7ec2b1963e36352b0774a19143b5f591540f7c06 - languageName: node - linkType: hard - "postcss@npm:^8.4.32": version: 8.5.9 resolution: "postcss@npm:8.5.9" @@ -11293,13 +11182,6 @@ __metadata: languageName: node linkType: hard -"shallowequal@npm:1.1.0": - version: 1.1.0 - resolution: "shallowequal@npm:1.1.0" - checksum: 10c0/b926efb51cd0f47aa9bc061add788a4a650550bbe50647962113a4579b60af2abe7b62f9b02314acc6f97151d4cf87033a2b15fc20852fae306d1a095215396c - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -11502,7 +11384,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf @@ -11818,26 +11700,6 @@ __metadata: languageName: node linkType: hard -"styled-components@npm:6.1.11": - version: 6.1.11 - resolution: "styled-components@npm:6.1.11" - dependencies: - "@emotion/is-prop-valid": "npm:1.2.2" - "@emotion/unitless": "npm:0.8.1" - "@types/stylis": "npm:4.2.5" - css-to-react-native: "npm:3.2.0" - csstype: "npm:3.1.3" - postcss: "npm:8.4.38" - shallowequal: "npm:1.1.0" - stylis: "npm:4.3.2" - tslib: "npm:2.6.2" - peerDependencies: - react: ">= 16.8.0" - react-dom: ">= 16.8.0" - checksum: 10c0/1d149a51d24f779bba700c8c23ec0538b2d2b57745ccd49d1cfdc2dfce8bcea21e8ff81fed1143d1b35d127cc591717a398da72ea6671abbf705432b13e59e56 - languageName: node - linkType: hard - "stylelint-config-recommended@npm:^18.0.0": version: 18.0.0 resolution: "stylelint-config-recommended@npm:18.0.0" @@ -11929,13 +11791,6 @@ __metadata: languageName: node linkType: hard -"stylis@npm:4.3.2": - version: 4.3.2 - resolution: "stylis@npm:4.3.2" - checksum: 10c0/0410e1404cbeee3388a9e17587875211ce2f014c8379af0d1e24ca55878867c9f1ccc7b0ce9a156ca53f5d6e301391a82b0645522a604674a378b3189a4a1994 - languageName: node - linkType: hard - "stylis@npm:^4.3.0": version: 4.3.6 resolution: "stylis@npm:4.3.6" @@ -12291,13 +12146,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb - languageName: node - linkType: hard - "tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1"