From 3cfc3c5af07a1019f5f5e76fdd3c4beb86d44718 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 5 Feb 2025 12:16:12 +0100 Subject: [PATCH 01/31] [useRenderer] Add public hook --- packages/react/src/index.ts | 1 + packages/react/src/use-renderer/index.ts | 1 + .../react/src/use-renderer/useRenderer.ts | 50 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 packages/react/src/use-renderer/index.ts create mode 100644 packages/react/src/use-renderer/useRenderer.ts diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 35abfbeda7..deabba51ee 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -25,3 +25,4 @@ export * from './tabs'; export * from './toggle'; export * from './toggle-group'; export * from './tooltip'; +export * from './use-renderer'; diff --git a/packages/react/src/use-renderer/index.ts b/packages/react/src/use-renderer/index.ts new file mode 100644 index 0000000000..a7b216f314 --- /dev/null +++ b/packages/react/src/use-renderer/index.ts @@ -0,0 +1 @@ +export { useRenderer } from './useRenderer'; diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts new file mode 100644 index 0000000000..c018e68161 --- /dev/null +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -0,0 +1,50 @@ +import * as React from 'react'; +import type { ComponentRenderFn } from '../utils/types'; +import { useComponentRenderer } from '../utils/useComponentRenderer'; +import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; + +/** + * Returns a function that renders a Base UI component. + */ +export function useRenderer< + State extends Record, + RenderedElementType extends Element, +>(settings: useRenderer.Settings) { + return useComponentRenderer(settings); +} + +namespace useRenderer { + export interface Settings { + /** + * The class name to apply to the rendered element. + * Can be a string or a function that accepts the state and returns a string. + */ + className?: string | ((state: State) => string); + /** + * The render prop or React element to override the default element. + */ + render: + | ComponentRenderFn, State> + | React.ReactElement> + | keyof typeof defaultRenderFunctions; + /** + * The state of the component. It will be used as a parameter for the render and className callbacks. + */ + state: State; + /** + * The ref to apply to the rendered element. + */ + ref?: React.Ref; + /** + * A function that returns props for the rendered element. + * It should accept and merge additional props. + */ + propGetter?: ( + externalProps: Record, + ) => React.HTMLAttributes & React.RefAttributes; + /** + * Additional props to be spread on the rendered element. + */ + extraProps?: Record; + } +} \ No newline at end of file From 9f32729573ec1c4e1a7f2bcf18b446fba7076d0b Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 5 Feb 2025 13:18:46 +0100 Subject: [PATCH 02/31] prettier --- packages/react/src/use-renderer/useRenderer.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index c018e68161..6370126db2 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -6,10 +6,9 @@ import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; /** * Returns a function that renders a Base UI component. */ -export function useRenderer< - State extends Record, - RenderedElementType extends Element, ->(settings: useRenderer.Settings) { +export function useRenderer, RenderedElementType extends Element>( + settings: useRenderer.Settings, +) { return useComponentRenderer(settings); } @@ -47,4 +46,4 @@ namespace useRenderer { */ extraProps?: Record; } -} \ No newline at end of file +} From 1b705fa8d5708a14521468866ce16f91f561cfa3 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 5 Feb 2025 14:31:35 +0100 Subject: [PATCH 03/31] update package.json exports --- packages/react/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react/package.json b/packages/react/package.json index b1eb7a428b..e99fe9bd98 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -56,6 +56,7 @@ "./tooltip": "./src/tooltip/index.ts", "./unstable-no-ssr": "./src/unstable-no-ssr/index.ts", "./unstable-use-media-query": "./src/unstable-use-media-query/index.ts", + "./use-renderer": "./src/use-renderer/index.ts", "./utils": "./src/utils/index.ts" }, "imports": { From 5e4fbcec930eca1d3f2f3d1ce569a2563ef052a1 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 09:08:47 +0100 Subject: [PATCH 04/31] apply review changes --- .../experiments/custom-components.module.css | 11 +++ .../experiments/custom-components.tsx | 72 +++++++++++++++++++ .../react/src/use-renderer/useRenderer.ts | 32 ++++++--- 3 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 docs/src/app/(private)/experiments/custom-components.module.css create mode 100644 docs/src/app/(private)/experiments/custom-components.tsx diff --git a/docs/src/app/(private)/experiments/custom-components.module.css b/docs/src/app/(private)/experiments/custom-components.module.css new file mode 100644 index 0000000000..0bd6e9b15f --- /dev/null +++ b/docs/src/app/(private)/experiments/custom-components.module.css @@ -0,0 +1,11 @@ +.Text { + font-size: 0.875rem; + line-height: 1.25rem; + color: #232323; + &[data-size='small'] { + font-size: 0.75rem; + } + &[data-size='large'] { + font-size: 2rem; + } +} diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx new file mode 100644 index 0000000000..646cb9a99d --- /dev/null +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -0,0 +1,72 @@ +'use client'; +import * as React from 'react'; +import { useRenderer } from '@base-ui-components/react/use-renderer'; +import styles from './custom-components.module.css'; + +type TextProps = { + weight?: 'light' | 'regular' | 'bold'; + className: any; + render?: any; + children: any; + style?: any; +}; + +const Text = React.forwardRef( + (props: TextProps, forwardedRef: React.ForwardedRef) => { + const { + className, + render, + style = {}, + weight = 'regular', + size = 'medium', + // Example state prop that we exclude from the style hooks + excludedProp = true, + ...otherProps + } = props; + + const fontWeight = { + light: 300, + regular: 400, + bold: 700, + }[weight]; + + const state = React.useMemo( + () => ({ weight, size, excludedProp }), + [weight, size, excludedProp], + ); + + const { renderElement } = useRenderer({ + render: render ?? 'p', + state, + className, + ref: forwardedRef, + props: { + ...otherProps, + style: { + ...style, + fontWeight, + }, + }, + excludedStyleHookStates: ['excludedProp'], + }); + + return renderElement(); + }, +); + +export default function ExampleText() { + return ( +
+ + Small text + + Default text + + Large text in bold + + } weight="bold"> + Text in bold + +
+ ); +} diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 6370126db2..8b22ee19c5 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -9,7 +9,26 @@ import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; export function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - return useComponentRenderer(settings); + const { className, render, state, ref, props, excludedStyleHookStates = [] } = settings; + + const customStyleHookMapping = React.useMemo(() => { + return excludedStyleHookStates.reduce((acc, key) => { + return { + ...acc, + [key]: () => null, + }; + }, {}); + }, [excludedStyleHookStates]); + + return useComponentRenderer({ + className, + render, + state, + ref, + extraProps: props, + propGetter: (props) => props, + ...(excludedStyleHookStates.length > 0 && { customStyleHookMapping }), + }); } namespace useRenderer { @@ -35,15 +54,12 @@ namespace useRenderer { */ ref?: React.Ref; /** - * A function that returns props for the rendered element. - * It should accept and merge additional props. + * Props to be spread on the rendered element. */ - propGetter?: ( - externalProps: Record, - ) => React.HTMLAttributes & React.RefAttributes; + props?: Record; /** - * Additional props to be spread on the rendered element. + * List of state keys that should not be generated as a styled hooks (data-attributes). */ - extraProps?: Record; + excludedStyleHookStates?: string[]; } } From b7c1d0996249b4814c1616a04cfe330fa77e707c Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 09:13:33 +0100 Subject: [PATCH 05/31] fix types --- docs/src/app/(private)/experiments/custom-components.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx index 646cb9a99d..9460f88810 100644 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -9,6 +9,8 @@ type TextProps = { render?: any; children: any; style?: any; + size: 'small' | 'medium' | 'large'; + excludedProp?: boolean; }; const Text = React.forwardRef( From 8038e580f487f3f7ac477a10817a556d74cbfe91 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 09:16:48 +0100 Subject: [PATCH 06/31] optional prop --- docs/src/app/(private)/experiments/custom-components.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx index 9460f88810..ab12535d25 100644 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -9,7 +9,7 @@ type TextProps = { render?: any; children: any; style?: any; - size: 'small' | 'medium' | 'large'; + size?: 'small' | 'medium' | 'large'; excludedProp?: boolean; }; From 0051604b16bcbcf087bb2b47464afcdb8dc69ed6 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 09:24:27 +0100 Subject: [PATCH 07/31] add tests --- .../src/use-renderer/useRenderer.test.tsx | 86 +++++++++++++++++++ .../react/src/use-renderer/useRenderer.ts | 4 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/use-renderer/useRenderer.test.tsx diff --git a/packages/react/src/use-renderer/useRenderer.test.tsx b/packages/react/src/use-renderer/useRenderer.test.tsx new file mode 100644 index 0000000000..0f3765454f --- /dev/null +++ b/packages/react/src/use-renderer/useRenderer.test.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { createRenderer } from '@mui/internal-test-utils'; +import { useRenderer } from '@base-ui-components/react/use-renderer'; + +describe('useRenderer', () => { + const { render } = createRenderer(); + + it('render props does not overwrite className in a render function when unspecified', async () => { + function TestComponent(props: { + render: useRenderer.Settings['render']; + className?: useRenderer.Settings['className']; + }) { + const { render: renderProp, className } = props; + const { renderElement } = useRenderer({ + render: renderProp, + state: {}, + className, + }); + return renderElement(); + } + + const { container } = await render( + } + />, + ); + + const element = container.firstElementChild; + + expect(element).to.have.attribute('class', 'my-span'); + }); + + it('includes data-attributes for all state members', async () => { + function TestComponent(props: { + render?: useRenderer.Settings['render']; + className?: useRenderer.Settings['className']; + size: 'small' | 'medium' | 'large'; + weight: 'light' | 'regular' | 'bold'; + }) { + const { render: renderProp, size, weight } = props; + const { renderElement } = useRenderer({ + render: renderProp ?? 'span', + state: { + size, + weight, + }, + }); + return renderElement(); + } + + const { container } = await render(); + + const element = container.firstElementChild; + + expect(element).to.have.attribute('data-size', 'large'); + expect(element).to.have.attribute('data-weight', 'bold'); + }); + + it('does not create data-attributes for the states specified in excludedStyleHookStates', async () => { + function TestComponent(props: { + render?: useRenderer.Settings['render']; + className?: useRenderer.Settings['className']; + size: 'small' | 'medium' | 'large'; + weight: 'light' | 'regular' | 'bold'; + }) { + const { render: renderProp, size, weight } = props; + const { renderElement } = useRenderer({ + render: renderProp ?? 'span', + state: { + size, + weight, + }, + excludedStyleHookStates: ['size'], + }); + return renderElement(); + } + + const { container } = await render(); + + const element = container.firstElementChild; + + expect(element).not.to.have.attribute('data-size', 'large'); + expect(element).to.have.attribute('data-weight', 'bold'); + }); +}); diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 8b22ee19c5..6866ea46a2 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -6,7 +6,7 @@ import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; /** * Returns a function that renders a Base UI component. */ -export function useRenderer, RenderedElementType extends Element>( +function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { const { className, render, state, ref, props, excludedStyleHookStates = [] } = settings; @@ -63,3 +63,5 @@ namespace useRenderer { excludedStyleHookStates?: string[]; } } + +export { useRenderer }; From 29fd26a04e6dc445044a61d0699ebf67657057ca Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 13:03:10 +0100 Subject: [PATCH 08/31] add test, change API --- .../src/use-renderer/useRenderer.test.tsx | 15 +++++++++++---- packages/react/src/use-renderer/useRenderer.ts | 18 +++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/react/src/use-renderer/useRenderer.test.tsx b/packages/react/src/use-renderer/useRenderer.test.tsx index 0f3765454f..464e4053eb 100644 --- a/packages/react/src/use-renderer/useRenderer.test.tsx +++ b/packages/react/src/use-renderer/useRenderer.test.tsx @@ -57,7 +57,7 @@ describe('useRenderer', () => { expect(element).to.have.attribute('data-weight', 'bold'); }); - it('does not create data-attributes for the states specified in excludedStyleHookStates', async () => { + it('respects the customStyleHookMapping config if provided', async () => { function TestComponent(props: { render?: useRenderer.Settings['render']; className?: useRenderer.Settings['className']; @@ -71,7 +71,14 @@ describe('useRenderer', () => { size, weight, }, - excludedStyleHookStates: ['size'], + customStyleHookMapping: { + size(value) { + return { [`data-size${value}`]: '' }; + }, + weight() { + return null; + }, + }, }); return renderElement(); } @@ -80,7 +87,7 @@ describe('useRenderer', () => { const element = container.firstElementChild; - expect(element).not.to.have.attribute('data-size', 'large'); - expect(element).to.have.attribute('data-weight', 'bold'); + expect(element).to.have.attribute('data-sizelarge', ''); + expect(element).not.to.have.attribute('data-weight'); }); }); diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 6866ea46a2..719793a21a 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -2,6 +2,7 @@ import * as React from 'react'; import type { ComponentRenderFn } from '../utils/types'; import { useComponentRenderer } from '../utils/useComponentRenderer'; import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; +import { CustomStyleHookMapping } from '../utils/getStyleHookProps'; /** * Returns a function that renders a Base UI component. @@ -9,16 +10,7 @@ import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, ref, props, excludedStyleHookStates = [] } = settings; - - const customStyleHookMapping = React.useMemo(() => { - return excludedStyleHookStates.reduce((acc, key) => { - return { - ...acc, - [key]: () => null, - }; - }, {}); - }, [excludedStyleHookStates]); + const { className, render, state, ref, props, customStyleHookMapping } = settings; return useComponentRenderer({ className, @@ -27,7 +19,7 @@ function useRenderer, RenderedElementType exte ref, extraProps: props, propGetter: (props) => props, - ...(excludedStyleHookStates.length > 0 && { customStyleHookMapping }), + customStyleHookMapping, }); } @@ -58,9 +50,9 @@ namespace useRenderer { */ props?: Record; /** - * List of state keys that should not be generated as a styled hooks (data-attributes). + * A mapping of state to style hooks. */ - excludedStyleHookStates?: string[]; + customStyleHookMapping?: CustomStyleHookMapping; } } From b87aadc16f45dc0671b23a8ae676748a9dc238f3 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Thu, 6 Feb 2025 13:47:25 +0100 Subject: [PATCH 09/31] lint & fix example --- docs/src/app/(private)/experiments/custom-components.tsx | 6 +++++- packages/react/src/use-renderer/useRenderer.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx index ab12535d25..454f0410ae 100644 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -49,7 +49,11 @@ const Text = React.forwardRef( fontWeight, }, }, - excludedStyleHookStates: ['excludedProp'], + customStyleHookMapping: { + excludedProp() { + return null; + }, + }, }); return renderElement(); diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 719793a21a..6dc75e7252 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -18,7 +18,7 @@ function useRenderer, RenderedElementType exte state, ref, extraProps: props, - propGetter: (props) => props, + propGetter: (x) => x, customStyleHookMapping, }); } From 116a673e7a7a22a0a6461ef85140c1943364333e Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 09:09:01 +0100 Subject: [PATCH 10/31] export more types, rename param --- .../experiments/custom-components.tsx | 25 +++++++++++++------ packages/react/src/use-renderer/index.ts | 1 + .../src/use-renderer/useRenderer.test.tsx | 2 +- .../react/src/use-renderer/useRenderer.ts | 22 +++++++++------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx index 454f0410ae..87ed585ef5 100644 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -1,15 +1,24 @@ 'use client'; import * as React from 'react'; -import { useRenderer } from '@base-ui-components/react/use-renderer'; +import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; import styles from './custom-components.module.css'; +type Weight = 'light' | 'regular' | 'bold'; +type Size = 'small' | 'medium' | 'large'; + +type TextState = { + weight: Weight; + size: Size; + excludedProp: boolean; +}; + type TextProps = { - weight?: 'light' | 'regular' | 'bold'; - className: any; - render?: any; - children: any; - style?: any; - size?: 'small' | 'medium' | 'large'; + className: string | ((state: TextState) => string); + weight?: Weight; + render?: RenderProp; + children: React.ReactNode; + style?: React.CSSProperties; + size?: Size; excludedProp?: boolean; }; @@ -49,7 +58,7 @@ const Text = React.forwardRef( fontWeight, }, }, - customStyleHookMapping: { + styleHookMapping: { excludedProp() { return null; }, diff --git a/packages/react/src/use-renderer/index.ts b/packages/react/src/use-renderer/index.ts index a7b216f314..2ac631354e 100644 --- a/packages/react/src/use-renderer/index.ts +++ b/packages/react/src/use-renderer/index.ts @@ -1 +1,2 @@ export { useRenderer } from './useRenderer'; +export * from './useRenderer'; diff --git a/packages/react/src/use-renderer/useRenderer.test.tsx b/packages/react/src/use-renderer/useRenderer.test.tsx index 464e4053eb..f94a773a90 100644 --- a/packages/react/src/use-renderer/useRenderer.test.tsx +++ b/packages/react/src/use-renderer/useRenderer.test.tsx @@ -71,7 +71,7 @@ describe('useRenderer', () => { size, weight, }, - customStyleHookMapping: { + styleHookMapping: { size(value) { return { [`data-size${value}`]: '' }; }, diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 6dc75e7252..c274af3440 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import type { ComponentRenderFn } from '../utils/types'; import { useComponentRenderer } from '../utils/useComponentRenderer'; import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; -import { CustomStyleHookMapping } from '../utils/getStyleHookProps'; +import { CustomStyleHookMapping as StyleHookMapping } from '../utils/getStyleHookProps'; /** * Returns a function that renders a Base UI component. @@ -10,7 +10,7 @@ import { CustomStyleHookMapping } from '../utils/getStyleHookProps'; function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, ref, props, customStyleHookMapping } = settings; + const { className, render, state, ref, props, styleHookMapping } = settings; return useComponentRenderer({ className, @@ -19,10 +19,15 @@ function useRenderer, RenderedElementType exte ref, extraProps: props, propGetter: (x) => x, - customStyleHookMapping, + customStyleHookMapping: styleHookMapping, }); } +type RenderProp = + | ComponentRenderFn, State> + | React.ReactElement> + | keyof typeof defaultRenderFunctions; + namespace useRenderer { export interface Settings { /** @@ -33,10 +38,7 @@ namespace useRenderer { /** * The render prop or React element to override the default element. */ - render: - | ComponentRenderFn, State> - | React.ReactElement> - | keyof typeof defaultRenderFunctions; + render: RenderProp; /** * The state of the component. It will be used as a parameter for the render and className callbacks. */ @@ -48,12 +50,14 @@ namespace useRenderer { /** * Props to be spread on the rendered element. */ - props?: Record; + props?: Record; /** * A mapping of state to style hooks. */ - customStyleHookMapping?: CustomStyleHookMapping; + styleHookMapping?: StyleHookMapping; } } +export type { ComponentRenderFn, StyleHookMapping, RenderProp }; + export { useRenderer }; From d50077d023e56c4de79940e534c60c916de92a1f Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 09:43:10 +0100 Subject: [PATCH 11/31] add docs page --- .../demos/usage/css-modules/index.module.css | 11 +++ .../demos/usage/css-modules/index.tsx | 76 +++++++++++++++++++ .../utils/use-renderer/demos/usage/index.ts | 2 + .../react/utils/use-renderer/page.mdx | 60 +++++++++++++++ docs/src/nav.ts | 4 + 5 files changed, 153 insertions(+) create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css new file mode 100644 index 0000000000..fc3b85ae0a --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css @@ -0,0 +1,11 @@ +.Text { + font-size: 0.875rem; + line-height: 1rem; + color: var(--color-gray-900); + &[data-size='small'] { + font-size: 0.75rem; + } + &[data-size='large'] { + font-size: 1.25rem; + } +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx new file mode 100644 index 0000000000..9d11f286fd --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx @@ -0,0 +1,76 @@ +'use client'; +import * as React from 'react'; +import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; +import styles from './index.module.css'; + +type Weight = 'light' | 'regular' | 'bold'; +type Size = 'small' | 'medium' | 'large'; + +type TextState = { + weight: Weight; + size: Size; +}; + +type TextProps = { + className: string | ((state: TextState) => string); + weight?: Weight; + render?: RenderProp; + children: React.ReactNode; + style?: React.CSSProperties; + size?: Size; + excludedProp?: boolean; +}; + +const Text = React.forwardRef( + (props: TextProps, forwardedRef: React.ForwardedRef) => { + const { + className, + render, + style = {}, + weight = 'regular', + size = 'medium', + ...otherProps + } = props; + + const fontWeight = { + light: 300, + regular: 400, + bold: 700, + }[weight]; + + const state = React.useMemo(() => ({ weight, size }), [weight, size]); + + const { renderElement } = useRenderer({ + render: render ?? 'p', + state, + className, + ref: forwardedRef, + props: { + ...otherProps, + style: { + ...style, + fontWeight, + }, + }, + }); + + return renderElement(); + }, +); + +export default function ExampleText() { + return ( +
+ + Small text + + Default text + + Large text + + }> + Bold text rendered in a strong tag + +
+ ); +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts new file mode 100644 index 0000000000..94264ab85d --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts @@ -0,0 +1,2 @@ +'use client'; +export { default as CssModules } from './css-modules'; diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx new file mode 100644 index 0000000000..67dca21a5a --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -0,0 +1,60 @@ +# useRenderer + +Utility for adding Base UI like features in a custom build components. + + +Base UI provides few common features across all components: + +- support for a [callback](/react/handbook/styling#css-classes) on the `className` prop where developers can generate classes based on the state of the components +- [render](/react/handbook/composition) prop that allows developers to override the default rendered element of the component +- adds data-attributes for the component's state that can be used as style hooks + +The `useRenderer` hook allows you to add these feature on your custom built components, so that you can provide consistenc experience across your library. + +## API reference + +### Input parameters + + string)', + description: + 'The class name to apply to the rendered element. Can be a string or a function that accepts the state and returns a string.', + }, + render: { + type: 'RenderProp', + description: 'The render prop or React element to override the default element.', + }, + state: { + type: 'State', + description: + 'The state of the component. It will be used as a parameter for the render and className callbacks.', + }, + ref: { + type: 'React.Ref', + description: 'The ref to apply to the rendered element.', + }, + props: { + type: 'Record', + description: 'Props to be spread on the rendered element.', + }, + styleHookMapping: { + type: 'StyleHookMapping', + description: 'A mapping of state to style hooks.', + }, + }} +/> + +### Return value + +The hook returns a function that when called returns the element that should be rendered. + +## Usage + +This is an example usage for creating a Text component that generates data-attributes based on its state, and have the support for the `render` prop and the `className` callback. + + diff --git a/docs/src/nav.ts b/docs/src/nav.ts index a90dec4fe1..d912011860 100644 --- a/docs/src/nav.ts +++ b/docs/src/nav.ts @@ -149,6 +149,10 @@ export const nav = [ label: 'Direction Provider', href: '/react/utils/direction-provider', }, + { + label: 'useRenderer', + href: '/react/utils/use-renderer', + }, ], }, ]; From 3c753fa8bb4148c31d53047af3c3c852ef3e7366 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Fri, 7 Feb 2025 10:23:42 +0100 Subject: [PATCH 12/31] Aaron's review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aarón García Hervás Signed-off-by: Marija Najdova --- .../react/utils/use-renderer/page.mdx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index 67dca21a5a..95c2ae68f3 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -1,18 +1,16 @@ # useRenderer -Utility for adding Base UI like features in a custom build components. +Utility for adding Base UI-like features to custom components. -Base UI provides few common features across all components: +The `useRenderer` hook allows you to support the same features Base UI provides across all components so that you can also have a consistent experience in your custom components: -- support for a [callback](/react/handbook/styling#css-classes) on the `className` prop where developers can generate classes based on the state of the components -- [render](/react/handbook/composition) prop that allows developers to override the default rendered element of the component -- adds data-attributes for the component's state that can be used as style hooks - -The `useRenderer` hook allows you to add these feature on your custom built components, so that you can provide consistenc experience across your library. +- A [render](/react/handbook/composition) prop to override the default rendered element. +- A [callback](/react/handbook/styling#css-classes) on the `className` prop that enables passing dynamically generated CSS classes based on the component's state. +- Adds [data-attributes](https://base-ui.com/react/handbook/styling#data-attributes) that map to the component's state that can be used as style hooks. ## API reference @@ -23,11 +21,11 @@ The `useRenderer` hook allows you to add these feature on your custom built comp className: { type: 'string | ((state: State) => string)', description: - 'The class name to apply to the rendered element. Can be a string or a function that accepts the state and returns a string.', + 'CSS class applied to the element, or a function that returns a class based on the component’s state.', }, render: { type: 'RenderProp', - description: 'The render prop or React element to override the default element.', + description: 'Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.', }, state: { type: 'State', From 2baa3dd423a9bc6a0e780c7cca2d8a1bb2014291 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 10:33:12 +0100 Subject: [PATCH 13/31] prettier --- .../(public)/(content)/react/utils/use-renderer/page.mdx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index 95c2ae68f3..4223ce4f49 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -1,10 +1,7 @@ # useRenderer Utility for adding Base UI-like features to custom components. - + The `useRenderer` hook allows you to support the same features Base UI provides across all components so that you can also have a consistent experience in your custom components: @@ -25,7 +22,8 @@ The `useRenderer` hook allows you to support the same features Base UI provides }, render: { type: 'RenderProp', - description: 'Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.', + description: + 'Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.', }, state: { type: 'State', From b94b8ce28627aee33a408af81cd519e2d30f9124 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 12:51:02 +0100 Subject: [PATCH 14/31] Use render element instead of string as a default render value --- docs/src/app/(private)/experiments/custom-components.tsx | 2 +- .../react/utils/use-renderer/demos/usage/css-modules/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx index 87ed585ef5..02bb31a8ff 100644 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ b/docs/src/app/(private)/experiments/custom-components.tsx @@ -47,7 +47,7 @@ const Text = React.forwardRef( ); const { renderElement } = useRenderer({ - render: render ?? 'p', + render: render ??

, state, className, ref: forwardedRef, diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx index 9d11f286fd..daebce3f71 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx @@ -41,7 +41,7 @@ const Text = React.forwardRef( const state = React.useMemo(() => ({ weight, size }), [weight, size]); const { renderElement } = useRenderer({ - render: render ?? 'p', + render: render ??

, state, className, ref: forwardedRef, From 6a8d16fad565eff0f5c830f1cd8dc1439b6717ac Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 16:52:21 +0100 Subject: [PATCH 15/31] move ref to props --- .../experiments/custom-components.module.css | 11 --- .../experiments/custom-components.tsx | 87 ------------------- .../demos/usage/css-modules/index.tsx | 59 ++++++------- .../react/utils/use-renderer/page.mdx | 4 - .../react/src/use-renderer/useRenderer.ts | 11 +-- 5 files changed, 32 insertions(+), 140 deletions(-) delete mode 100644 docs/src/app/(private)/experiments/custom-components.module.css delete mode 100644 docs/src/app/(private)/experiments/custom-components.tsx diff --git a/docs/src/app/(private)/experiments/custom-components.module.css b/docs/src/app/(private)/experiments/custom-components.module.css deleted file mode 100644 index 0bd6e9b15f..0000000000 --- a/docs/src/app/(private)/experiments/custom-components.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.Text { - font-size: 0.875rem; - line-height: 1.25rem; - color: #232323; - &[data-size='small'] { - font-size: 0.75rem; - } - &[data-size='large'] { - font-size: 2rem; - } -} diff --git a/docs/src/app/(private)/experiments/custom-components.tsx b/docs/src/app/(private)/experiments/custom-components.tsx deleted file mode 100644 index 02bb31a8ff..0000000000 --- a/docs/src/app/(private)/experiments/custom-components.tsx +++ /dev/null @@ -1,87 +0,0 @@ -'use client'; -import * as React from 'react'; -import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; -import styles from './custom-components.module.css'; - -type Weight = 'light' | 'regular' | 'bold'; -type Size = 'small' | 'medium' | 'large'; - -type TextState = { - weight: Weight; - size: Size; - excludedProp: boolean; -}; - -type TextProps = { - className: string | ((state: TextState) => string); - weight?: Weight; - render?: RenderProp; - children: React.ReactNode; - style?: React.CSSProperties; - size?: Size; - excludedProp?: boolean; -}; - -const Text = React.forwardRef( - (props: TextProps, forwardedRef: React.ForwardedRef) => { - const { - className, - render, - style = {}, - weight = 'regular', - size = 'medium', - // Example state prop that we exclude from the style hooks - excludedProp = true, - ...otherProps - } = props; - - const fontWeight = { - light: 300, - regular: 400, - bold: 700, - }[weight]; - - const state = React.useMemo( - () => ({ weight, size, excludedProp }), - [weight, size, excludedProp], - ); - - const { renderElement } = useRenderer({ - render: render ??

, - state, - className, - ref: forwardedRef, - props: { - ...otherProps, - style: { - ...style, - fontWeight, - }, - }, - styleHookMapping: { - excludedProp() { - return null; - }, - }, - }); - - return renderElement(); - }, -); - -export default function ExampleText() { - return ( -

- - Small text - - Default text - - Large text in bold - - } weight="bold"> - Text in bold - -
- ); -} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx index daebce3f71..c142e29fa9 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx @@ -21,42 +21,39 @@ type TextProps = { excludedProp?: boolean; }; -const Text = React.forwardRef( - (props: TextProps, forwardedRef: React.ForwardedRef) => { - const { - className, - render, - style = {}, - weight = 'regular', - size = 'medium', - ...otherProps - } = props; +function Text(props: TextProps) { + const { + className, + render, + style = {}, + weight = 'regular', + size = 'medium', + ...otherProps + } = props; - const fontWeight = { - light: 300, - regular: 400, - bold: 700, - }[weight]; + const fontWeight = { + light: 300, + regular: 400, + bold: 700, + }[weight]; - const state = React.useMemo(() => ({ weight, size }), [weight, size]); + const state = React.useMemo(() => ({ weight, size }), [weight, size]); - const { renderElement } = useRenderer({ - render: render ??

, - state, - className, - ref: forwardedRef, - props: { - ...otherProps, - style: { - ...style, - fontWeight, - }, + const { renderElement } = useRenderer({ + render: render ??

, + state, + className, + props: { + ...otherProps, + style: { + ...style, + fontWeight, }, - }); + }, + }); - return renderElement(); - }, -); + return renderElement(); +} export default function ExampleText() { return ( diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index 4223ce4f49..a415dea929 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -30,10 +30,6 @@ The `useRenderer` hook allows you to support the same features Base UI provides description: 'The state of the component. It will be used as a parameter for the render and className callbacks.', }, - ref: { - type: 'React.Ref', - description: 'The ref to apply to the rendered element.', - }, props: { type: 'Record', description: 'Props to be spread on the rendered element.', diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index c274af3440..654a45b8fb 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -10,14 +10,15 @@ import { CustomStyleHookMapping as StyleHookMapping } from '../utils/getStyleHoo function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, ref, props, styleHookMapping } = settings; + const { className, render, state, props, styleHookMapping } = settings; + const { ref, ...extraProps } = props ?? {}; return useComponentRenderer({ className, render, state, - ref, - extraProps: props, + ref: ref as React.Ref, + extraProps, propGetter: (x) => x, customStyleHookMapping: styleHookMapping, }); @@ -43,10 +44,6 @@ namespace useRenderer { * The state of the component. It will be used as a parameter for the render and className callbacks. */ state: State; - /** - * The ref to apply to the rendered element. - */ - ref?: React.Ref; /** * Props to be spread on the rendered element. */ From 2a9621f526159f259602dbdb6e5163239422b624 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 7 Feb 2025 16:56:17 +0100 Subject: [PATCH 16/31] rename style hooks mapping --- .../(content)/react/utils/use-renderer/page.mdx | 4 ++-- packages/react/src/use-renderer/useRenderer.test.tsx | 2 +- packages/react/src/use-renderer/useRenderer.ts | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index a415dea929..c5af62c9b9 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -34,8 +34,8 @@ The `useRenderer` hook allows you to support the same features Base UI provides type: 'Record', description: 'Props to be spread on the rendered element.', }, - styleHookMapping: { - type: 'StyleHookMapping', + stateDataAttributes: { + type: 'StateDataAttributes', description: 'A mapping of state to style hooks.', }, }} diff --git a/packages/react/src/use-renderer/useRenderer.test.tsx b/packages/react/src/use-renderer/useRenderer.test.tsx index f94a773a90..9ad49d7e1d 100644 --- a/packages/react/src/use-renderer/useRenderer.test.tsx +++ b/packages/react/src/use-renderer/useRenderer.test.tsx @@ -71,7 +71,7 @@ describe('useRenderer', () => { size, weight, }, - styleHookMapping: { + stateDataAttributes: { size(value) { return { [`data-size${value}`]: '' }; }, diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 654a45b8fb..826df1f23a 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import type { ComponentRenderFn } from '../utils/types'; import { useComponentRenderer } from '../utils/useComponentRenderer'; import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; -import { CustomStyleHookMapping as StyleHookMapping } from '../utils/getStyleHookProps'; +import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyleHookProps'; /** * Returns a function that renders a Base UI component. @@ -10,7 +10,7 @@ import { CustomStyleHookMapping as StyleHookMapping } from '../utils/getStyleHoo function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, props, styleHookMapping } = settings; + const { className, render, state, props, stateDataAttributes } = settings; const { ref, ...extraProps } = props ?? {}; return useComponentRenderer({ @@ -20,7 +20,7 @@ function useRenderer, RenderedElementType exte ref: ref as React.Ref, extraProps, propGetter: (x) => x, - customStyleHookMapping: styleHookMapping, + customStyleHookMapping: stateDataAttributes, }); } @@ -49,12 +49,12 @@ namespace useRenderer { */ props?: Record; /** - * A mapping of state to style hooks. + * A mapping of state to data attributes. */ - styleHookMapping?: StyleHookMapping; + stateDataAttributes?: StateDataAttributes; } } -export type { ComponentRenderFn, StyleHookMapping, RenderProp }; +export type { ComponentRenderFn, StateDataAttributes, RenderProp }; export { useRenderer }; From c523e6f436c2c97ee275840dcc27885d5ebfab4a Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 09:44:16 +0100 Subject: [PATCH 17/31] fix type usage --- packages/react/src/use-renderer/useRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 826df1f23a..c8f7fd0e69 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -47,7 +47,7 @@ namespace useRenderer { /** * Props to be spread on the rendered element. */ - props?: Record; + props?: Record & { ref?: React.Ref }; /** * A mapping of state to data attributes. */ From ec020618ccfc7c81015a2fb2cde93f3e24634629 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 11:25:34 +0100 Subject: [PATCH 18/31] add component & demo for comparison --- .../component/css-modules/index.module.css | 11 ++ .../demos/component/css-modules/index.tsx | 76 +++++++++++++ .../use-renderer/demos/component/index.ts | 2 + .../react/utils/use-renderer/page.mdx | 4 + packages/react/package.json | 1 + packages/react/src/index.ts | 1 + packages/react/src/slot/Slot.test.tsx | 13 +++ packages/react/src/slot/Slot.tsx | 101 ++++++++++++++++++ packages/react/src/slot/index.tsx | 1 + 9 files changed, 210 insertions(+) create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts create mode 100644 packages/react/src/slot/Slot.test.tsx create mode 100644 packages/react/src/slot/Slot.tsx create mode 100644 packages/react/src/slot/index.tsx diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css new file mode 100644 index 0000000000..fc3b85ae0a --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css @@ -0,0 +1,11 @@ +.Text { + font-size: 0.875rem; + line-height: 1rem; + color: var(--color-gray-900); + &[data-size='small'] { + font-size: 0.75rem; + } + &[data-size='large'] { + font-size: 1.25rem; + } +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx new file mode 100644 index 0000000000..fa2975a612 --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx @@ -0,0 +1,76 @@ +'use client'; +import * as React from 'react'; +// Ignore the import, it will be moved to the Slot component +import { RenderProp } from '@base-ui-components/react/use-renderer'; +import { Slot } from '@base-ui-components/react/slot'; +import styles from './index.module.css'; + +type Weight = 'light' | 'regular' | 'bold'; +type Size = 'small' | 'medium' | 'large'; + +// type TextState = { +// weight: Weight; +// size: Size; +// }; + +// TODO: fix the types of the generic +type TextProps = { + className?: string | ((state: Record) => string); + weight?: Weight; + render?: RenderProp>; + children: React.ReactNode; + style?: React.CSSProperties; + size?: Size; + excludedProp?: boolean; +}; + +function Text(props: TextProps) { + const { + className, + render, + style = {}, + weight = 'regular', + size = 'medium', + ...otherProps + } = props; + + const fontWeight = { + light: 300, + regular: 400, + bold: 700, + }[weight]; + + const state = React.useMemo(() => ({ weight, size }), [weight, size]); + + return ( + } + state={state} + className={className} + props={{ + ...otherProps, + style: { + ...style, + fontWeight, + }, + }} + /> + ); +} + +export default function ExampleText() { + return ( +

+ + Small text + + Default text + + Large text + + }> + Bold text rendered in a strong tag + +
+ ); +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts new file mode 100644 index 0000000000..94264ab85d --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts @@ -0,0 +1,2 @@ +'use client'; +export { default as CssModules } from './css-modules'; diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index c5af62c9b9..f5fa9a801f 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -50,3 +50,7 @@ The hook returns a function that when called returns the element that should be This is an example usage for creating a Text component that generates data-attributes based on its state, and have the support for the `render` prop and the `className` callback. + +Alternatevly, you can use the Slot component + + diff --git a/packages/react/package.json b/packages/react/package.json index e99fe9bd98..59c6e27118 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -49,6 +49,7 @@ "./select": "./src/select/index.ts", "./separator": "./src/separator/index.ts", "./slider": "./src/slider/index.ts", + "./slot": "./src/slot/index.ts", "./switch": "./src/switch/index.ts", "./tabs": "./src/tabs/index.ts", "./toggle": "./src/toggle/index.ts", diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index deabba51ee..875f57278c 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -19,6 +19,7 @@ export * from './radio-group'; export * from './scroll-area'; export * from './select'; export * from './separator'; +export * from './slot'; export * from './slider'; export * from './switch'; export * from './tabs'; diff --git a/packages/react/src/slot/Slot.test.tsx b/packages/react/src/slot/Slot.test.tsx new file mode 100644 index 0000000000..50dc864c10 --- /dev/null +++ b/packages/react/src/slot/Slot.test.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { Slot } from '@base-ui-components/react/slot'; +import { createRenderer, describeConformance } from '#test-utils'; + +describe('', () => { + const { render } = createRenderer(); + + describeConformance(, () => ({ + render, + refInstanceof: window.HTMLDivElement, + })); +}); diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx new file mode 100644 index 0000000000..8db8b9e68e --- /dev/null +++ b/packages/react/src/slot/Slot.tsx @@ -0,0 +1,101 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { useComponentRenderer } from '../utils/useComponentRenderer'; +import { useForkRef } from '../utils'; +import type { ComponentRenderFn } from '../utils/types'; +import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; +import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyleHookProps'; + +const Slot = React.forwardRef(function SlotComponent< + State extends Record, + RenderedElementType extends Element, +>( + inProps: Slot.Props, + forwardedRef: React.ForwardedRef, +) { + const { className, render =
, state, props, stateDataAttributes } = inProps; + const { ref, ...extraProps } = props ?? {}; + const finalRef = useForkRef(ref, forwardedRef); + + const { renderElement } = useComponentRenderer({ + className, + render, + state, + ref: finalRef as React.Ref, + extraProps, + propGetter: (x) => x, + customStyleHookMapping: stateDataAttributes, + }); + + return renderElement(); +}); + +type RenderProp = + | ComponentRenderFn, State> + | React.ReactElement> + | keyof typeof defaultRenderFunctions; + +namespace Slot { + export interface Props, RenderedElementType extends Element> { + /** + * The class name to apply to the rendered element. + * Can be a string or a function that accepts the state and returns a string. + */ + className?: string | ((state: State) => string); + /** + * The render prop or React element to override the default element. + */ + render?: RenderProp; + /** + * The state of the component. It will be used as a parameter for the render and className callbacks. + */ + state: State; + /** + * Props to be spread on the rendered element. + */ + props?: Record & { ref?: React.Ref }; + /** + * A mapping of state to data attributes. + */ + stateDataAttributes?: StateDataAttributes; + } +} + +export { Slot }; + +Slot.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * CSS class applied to the element, or a function that + * returns a class based on the component’s state. + */ + className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The orientation of the separator. + * @default 'horizontal' + */ + orientation: PropTypes.oneOf(['horizontal', 'vertical']), + /** + * Allows you to replace the component’s HTML element + * with a different tag, or compose it with another component. + * + * Accepts a `ReactElement` or a function that returns the element to render. + */ + render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), + /** + * The state of the component. It will be used as a parameter for the render and className callbacks. + */ + state: PropTypes.object, + /** + * Props to be spread on the rendered element. + */ + props: PropTypes.object, +} as any; diff --git a/packages/react/src/slot/index.tsx b/packages/react/src/slot/index.tsx new file mode 100644 index 0000000000..f521c503f4 --- /dev/null +++ b/packages/react/src/slot/index.tsx @@ -0,0 +1 @@ +export { Slot } from './Slot'; From 034b74771746793905675e58e1b1138c4a73021c Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 11:30:10 +0100 Subject: [PATCH 19/31] optional state --- packages/react/src/slot/Slot.test.tsx | 2 +- packages/react/src/slot/Slot.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react/src/slot/Slot.test.tsx b/packages/react/src/slot/Slot.test.tsx index 50dc864c10..54fced6cf0 100644 --- a/packages/react/src/slot/Slot.test.tsx +++ b/packages/react/src/slot/Slot.test.tsx @@ -6,7 +6,7 @@ import { createRenderer, describeConformance } from '#test-utils'; describe('', () => { const { render } = createRenderer(); - describeConformance(, () => ({ + describeConformance(, () => ({ render, refInstanceof: window.HTMLDivElement, })); diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx index 8db8b9e68e..d7ca1778f6 100644 --- a/packages/react/src/slot/Slot.tsx +++ b/packages/react/src/slot/Slot.tsx @@ -14,7 +14,7 @@ const Slot = React.forwardRef(function SlotComponent< inProps: Slot.Props, forwardedRef: React.ForwardedRef, ) { - const { className, render =
, state, props, stateDataAttributes } = inProps; + const { className, render =
, state = {} as State, props, stateDataAttributes } = inProps; const { ref, ...extraProps } = props ?? {}; const finalRef = useForkRef(ref, forwardedRef); @@ -50,7 +50,7 @@ namespace Slot { /** * The state of the component. It will be used as a parameter for the render and className callbacks. */ - state: State; + state?: State; /** * Props to be spread on the rendered element. */ From f0674107c62fbd3b1290efbac658ee0506e03d18 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 11:30:31 +0100 Subject: [PATCH 20/31] remove test for now --- packages/react/src/slot/Slot.test.tsx | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 packages/react/src/slot/Slot.test.tsx diff --git a/packages/react/src/slot/Slot.test.tsx b/packages/react/src/slot/Slot.test.tsx deleted file mode 100644 index 54fced6cf0..0000000000 --- a/packages/react/src/slot/Slot.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { Slot } from '@base-ui-components/react/slot'; -import { createRenderer, describeConformance } from '#test-utils'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - render, - refInstanceof: window.HTMLDivElement, - })); -}); From 7dcdc5ea096c45fdf295847ae6226912efcf1150 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 11:57:51 +0100 Subject: [PATCH 21/31] simplify demo --- .../component/css-modules/index.module.css | 7 +++++++ .../demos/component/css-modules/index.tsx | 17 +---------------- packages/react/src/slot/Slot.tsx | 8 +++----- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css index fc3b85ae0a..1a1bc04784 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css @@ -1,6 +1,7 @@ .Text { font-size: 0.875rem; line-height: 1rem; + font-weight: 400; color: var(--color-gray-900); &[data-size='small'] { font-size: 0.75rem; @@ -8,4 +9,10 @@ &[data-size='large'] { font-size: 1.25rem; } + &[data-weight='light'] { + font-weight: 300; + } + &[data-weight='bold'] { + font-weight: 700; + } } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx index fa2975a612..ce4164faba 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx @@ -26,34 +26,19 @@ type TextProps = { function Text(props: TextProps) { const { - className, render, - style = {}, weight = 'regular', size = 'medium', ...otherProps } = props; - const fontWeight = { - light: 300, - regular: 400, - bold: 700, - }[weight]; - const state = React.useMemo(() => ({ weight, size }), [weight, size]); return ( } state={state} - className={className} - props={{ - ...otherProps, - style: { - ...style, - fontWeight, - }, - }} + {...otherProps} /> ); } diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx index d7ca1778f6..15ac978e40 100644 --- a/packages/react/src/slot/Slot.tsx +++ b/packages/react/src/slot/Slot.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useComponentRenderer } from '../utils/useComponentRenderer'; import { useForkRef } from '../utils'; +import type { BaseUIComponentProps } from '../utils/types'; import type { ComponentRenderFn } from '../utils/types'; import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyleHookProps'; @@ -14,7 +15,7 @@ const Slot = React.forwardRef(function SlotComponent< inProps: Slot.Props, forwardedRef: React.ForwardedRef, ) { - const { className, render =
, state = {} as State, props, stateDataAttributes } = inProps; + const { className, render =
, state = {} as State, stateDataAttributes, ...props } = inProps; const { ref, ...extraProps } = props ?? {}; const finalRef = useForkRef(ref, forwardedRef); @@ -51,14 +52,11 @@ namespace Slot { * The state of the component. It will be used as a parameter for the render and className callbacks. */ state?: State; - /** - * Props to be spread on the rendered element. - */ - props?: Record & { ref?: React.Ref }; /** * A mapping of state to data attributes. */ stateDataAttributes?: StateDataAttributes; + ref?: React.Ref } } From 15728adba77fee94090c2db874f8a10a59c530b9 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Wed, 12 Feb 2025 13:19:43 +0100 Subject: [PATCH 22/31] simlify hook demo --- .../demos/component/css-modules/index.tsx | 6 ------ .../demos/usage/css-modules/index.module.css | 7 +++++++ .../demos/usage/css-modules/index.tsx | 17 +---------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx index ce4164faba..09455ea04b 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx @@ -8,12 +8,6 @@ import styles from './index.module.css'; type Weight = 'light' | 'regular' | 'bold'; type Size = 'small' | 'medium' | 'large'; -// type TextState = { -// weight: Weight; -// size: Size; -// }; - -// TODO: fix the types of the generic type TextProps = { className?: string | ((state: Record) => string); weight?: Weight; diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css index fc3b85ae0a..1a1bc04784 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css @@ -1,6 +1,7 @@ .Text { font-size: 0.875rem; line-height: 1rem; + font-weight: 400; color: var(--color-gray-900); &[data-size='small'] { font-size: 0.75rem; @@ -8,4 +9,10 @@ &[data-size='large'] { font-size: 1.25rem; } + &[data-weight='light'] { + font-weight: 300; + } + &[data-weight='bold'] { + font-weight: 700; + } } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx index c142e29fa9..977d95443a 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx @@ -16,40 +16,25 @@ type TextProps = { weight?: Weight; render?: RenderProp; children: React.ReactNode; - style?: React.CSSProperties; size?: Size; - excludedProp?: boolean; }; function Text(props: TextProps) { const { className, render, - style = {}, weight = 'regular', size = 'medium', ...otherProps } = props; - const fontWeight = { - light: 300, - regular: 400, - bold: 700, - }[weight]; - const state = React.useMemo(() => ({ weight, size }), [weight, size]); const { renderElement } = useRenderer({ render: render ??

, state, className, - props: { - ...otherProps, - style: { - ...style, - fontWeight, - }, - }, + props: otherProps, }); return renderElement(); From 56ff6fe9989aa1c2b826b212d326505f273b3c8e Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 11:28:24 +0100 Subject: [PATCH 23/31] StateDataAttributes -> stateAttributesMap --- .../(public)/(content)/react/utils/use-renderer/page.mdx | 2 +- packages/react/src/slot/Slot.tsx | 6 +++--- packages/react/src/use-renderer/useRenderer.test.tsx | 2 +- packages/react/src/use-renderer/useRenderer.ts | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index f5fa9a801f..32e7fcb1ae 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -34,7 +34,7 @@ The `useRenderer` hook allows you to support the same features Base UI provides type: 'Record', description: 'Props to be spread on the rendered element.', }, - stateDataAttributes: { + stateAttributesMap: { type: 'StateDataAttributes', description: 'A mapping of state to style hooks.', }, diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx index 15ac978e40..1f153ad17f 100644 --- a/packages/react/src/slot/Slot.tsx +++ b/packages/react/src/slot/Slot.tsx @@ -15,7 +15,7 @@ const Slot = React.forwardRef(function SlotComponent< inProps: Slot.Props, forwardedRef: React.ForwardedRef, ) { - const { className, render =

, state = {} as State, stateDataAttributes, ...props } = inProps; + const { className, render =
, state = {} as State, stateAttributesMap, ...props } = inProps; const { ref, ...extraProps } = props ?? {}; const finalRef = useForkRef(ref, forwardedRef); @@ -26,7 +26,7 @@ const Slot = React.forwardRef(function SlotComponent< ref: finalRef as React.Ref, extraProps, propGetter: (x) => x, - customStyleHookMapping: stateDataAttributes, + customStyleHookMapping: stateAttributesMap, }); return renderElement(); @@ -55,7 +55,7 @@ namespace Slot { /** * A mapping of state to data attributes. */ - stateDataAttributes?: StateDataAttributes; + stateAttributesMap?: StateDataAttributes; ref?: React.Ref } } diff --git a/packages/react/src/use-renderer/useRenderer.test.tsx b/packages/react/src/use-renderer/useRenderer.test.tsx index 9ad49d7e1d..8ed89767e1 100644 --- a/packages/react/src/use-renderer/useRenderer.test.tsx +++ b/packages/react/src/use-renderer/useRenderer.test.tsx @@ -71,7 +71,7 @@ describe('useRenderer', () => { size, weight, }, - stateDataAttributes: { + stateAttributesMap: { size(value) { return { [`data-size${value}`]: '' }; }, diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index c8f7fd0e69..21ad78f73b 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -10,7 +10,7 @@ import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyle function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, props, stateDataAttributes } = settings; + const { className, render, state, props, stateAttributesMap } = settings; const { ref, ...extraProps } = props ?? {}; return useComponentRenderer({ @@ -20,7 +20,7 @@ function useRenderer, RenderedElementType exte ref: ref as React.Ref, extraProps, propGetter: (x) => x, - customStyleHookMapping: stateDataAttributes, + customStyleHookMapping: stateAttributesMap, }); } @@ -51,7 +51,7 @@ namespace useRenderer { /** * A mapping of state to data attributes. */ - stateDataAttributes?: StateDataAttributes; + stateAttributesMap?: StateDataAttributes; } } From df8d7736b08eb43e6f85346ad79cebbe40fba0ba Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 11:49:15 +0100 Subject: [PATCH 24/31] improve state --- .../demos/component/css-modules/index.tsx | 15 ++------------ .../demos/usage/css-modules/index.module.css | 3 +++ .../demos/usage/css-modules/index.tsx | 20 +++++++++++++++++-- packages/react/src/slot/Slot.tsx | 10 ++++++++-- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx index 09455ea04b..c040e8c063 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx @@ -19,22 +19,11 @@ type TextProps = { }; function Text(props: TextProps) { - const { - render, - weight = 'regular', - size = 'medium', - ...otherProps - } = props; + const { render, weight = 'regular', size = 'medium', ...otherProps } = props; const state = React.useMemo(() => ({ weight, size }), [weight, size]); - return ( - } - state={state} - {...otherProps} - /> - ); + return } state={state} {...otherProps} />; } export default function ExampleText() { diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css index 1a1bc04784..5bc050580f 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css @@ -15,4 +15,7 @@ &[data-weight='bold'] { font-weight: 700; } + &[data-color='blue'] { + color: var(--color-blue); + } } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx index 977d95443a..d429ef3507 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx @@ -5,16 +5,19 @@ import styles from './index.module.css'; type Weight = 'light' | 'regular' | 'bold'; type Size = 'small' | 'medium' | 'large'; +type Color = 'grey' | 'blue'; type TextState = { weight: Weight; size: Size; + color: Color; }; type TextProps = { className: string | ((state: TextState) => string); weight?: Weight; render?: RenderProp; + onClick?: (event: React.MouseEvent) => void; children: React.ReactNode; size?: Size; }; @@ -25,16 +28,29 @@ function Text(props: TextProps) { render, weight = 'regular', size = 'medium', + onClick, ...otherProps } = props; + const [color, setColor] = React.useState('grey'); - const state = React.useMemo(() => ({ weight, size }), [weight, size]); + const onClickHandler = (event: React.MouseEvent) => { + setColor(color === 'grey' ? 'blue' : 'grey'); + onClick?.(event); + }; + + const state = React.useMemo( + () => ({ weight, size, color }), + [weight, size, color], + ); const { renderElement } = useRenderer({ render: render ??

, state, className, - props: otherProps, + props: { + ...otherProps, + onClick: onClickHandler, + }, }); return renderElement(); diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx index 1f153ad17f..99a8e52cbc 100644 --- a/packages/react/src/slot/Slot.tsx +++ b/packages/react/src/slot/Slot.tsx @@ -15,7 +15,13 @@ const Slot = React.forwardRef(function SlotComponent< inProps: Slot.Props, forwardedRef: React.ForwardedRef, ) { - const { className, render =

, state = {} as State, stateAttributesMap, ...props } = inProps; + const { + className, + render =
, + state = {} as State, + stateAttributesMap, + ...props + } = inProps; const { ref, ...extraProps } = props ?? {}; const finalRef = useForkRef(ref, forwardedRef); @@ -56,7 +62,7 @@ namespace Slot { * A mapping of state to data attributes. */ stateAttributesMap?: StateDataAttributes; - ref?: React.Ref + ref?: React.Ref; } } From b813b3e9ea9b86d87e9feca4d681bfcd625f59fb Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 11:50:53 +0100 Subject: [PATCH 25/31] pointer cursor --- .../utils/use-renderer/demos/usage/css-modules/index.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css index 5bc050580f..db928e4cb2 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css @@ -1,4 +1,5 @@ .Text { + cursor: pointer; font-size: 0.875rem; line-height: 1rem; font-weight: 400; From 9515c7755dcd690f4fb50e4bec46e7a548ca09ed Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Fri, 14 Feb 2025 11:51:50 +0100 Subject: [PATCH 26/31] Update packages/react/src/use-renderer/index.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Dudak Signed-off-by: Marija Najdova --- packages/react/src/use-renderer/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/use-renderer/index.ts b/packages/react/src/use-renderer/index.ts index 2ac631354e..97d7757640 100644 --- a/packages/react/src/use-renderer/index.ts +++ b/packages/react/src/use-renderer/index.ts @@ -1,2 +1 @@ -export { useRenderer } from './useRenderer'; export * from './useRenderer'; From 7103e08dadf17ecf8c0c4dd5bfde341fc9615cbd Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 13:14:57 +0100 Subject: [PATCH 27/31] improve docs page --- .../demos/component/css-modules/index.tsx | 44 -------------- .../css-modules/index.module.css | 8 +-- .../data-attributes/css-modules/index.tsx | 58 +++++++++++++++++++ .../{component => data-attributes}/index.ts | 0 .../demos/render/css-modules/index.module.css | 10 ++++ .../demos/render/css-modules/index.tsx | 32 ++++++++++ .../demos/{usage => render}/index.ts | 0 .../css-modules/index.module.css | 12 +--- .../css-modules/index.tsx | 30 +++------- .../demos/state-attributes-map/index.ts | 2 + .../react/utils/use-renderer/page.mdx | 12 ++-- .../react/src/use-renderer/useRenderer.ts | 6 +- 12 files changed, 128 insertions(+), 86 deletions(-) delete mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx rename docs/src/app/(public)/(content)/react/utils/use-renderer/demos/{component => data-attributes}/css-modules/index.module.css (68%) create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.tsx rename docs/src/app/(public)/(content)/react/utils/use-renderer/demos/{component => data-attributes}/index.ts (100%) create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.tsx rename docs/src/app/(public)/(content)/react/utils/use-renderer/demos/{usage => render}/index.ts (100%) rename docs/src/app/(public)/(content)/react/utils/use-renderer/demos/{usage => state-attributes-map}/css-modules/index.module.css (54%) rename docs/src/app/(public)/(content)/react/utils/use-renderer/demos/{usage => state-attributes-map}/css-modules/index.tsx (65%) create mode 100644 docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/index.ts diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx deleted file mode 100644 index c040e8c063..0000000000 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client'; -import * as React from 'react'; -// Ignore the import, it will be moved to the Slot component -import { RenderProp } from '@base-ui-components/react/use-renderer'; -import { Slot } from '@base-ui-components/react/slot'; -import styles from './index.module.css'; - -type Weight = 'light' | 'regular' | 'bold'; -type Size = 'small' | 'medium' | 'large'; - -type TextProps = { - className?: string | ((state: Record) => string); - weight?: Weight; - render?: RenderProp>; - children: React.ReactNode; - style?: React.CSSProperties; - size?: Size; - excludedProp?: boolean; -}; - -function Text(props: TextProps) { - const { render, weight = 'regular', size = 'medium', ...otherProps } = props; - - const state = React.useMemo(() => ({ weight, size }), [weight, size]); - - return } state={state} {...otherProps} />; -} - -export default function ExampleText() { - return ( -
- - Small text - - Default text - - Large text - - }> - Bold text rendered in a strong tag - -
- ); -} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.module.css similarity index 68% rename from docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css rename to docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.module.css index 1a1bc04784..da17fc411a 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.module.css @@ -1,4 +1,5 @@ .Text { + cursor: pointer; font-size: 0.875rem; line-height: 1rem; font-weight: 400; @@ -9,10 +10,7 @@ &[data-size='large'] { font-size: 1.25rem; } - &[data-weight='light'] { - font-weight: 300; - } - &[data-weight='bold'] { - font-weight: 700; + &[data-color='active'] { + color: var(--color-blue); } } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.tsx new file mode 100644 index 0000000000..cc20bfc564 --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/css-modules/index.tsx @@ -0,0 +1,58 @@ +'use client'; +import * as React from 'react'; +import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; +import styles from './index.module.css'; + +type Size = 'small' | 'medium' | 'large'; +type Color = 'default' | 'active'; + +type TextState = { + size: Size; + color: Color; +}; + +type TextProps = { + className: string | ((state: TextState) => string); + render?: RenderProp; + onClick?: (event: React.MouseEvent) => void; + children: React.ReactNode; + size?: Size; +}; + +function Text(props: TextProps) { + const { className, render, size = 'medium', onClick, ...otherProps } = props; + const [color, setColor] = React.useState('default'); + + const onClickHandler = (event: React.MouseEvent) => { + setColor(color === 'default' ? 'active' : 'default'); + onClick?.(event); + }; + + const state = React.useMemo(() => ({ size, color }), [size, color]); + + const { renderElement } = useRenderer({ + render: render ??

, + state, + className, + props: { + ...otherProps, + onClick: onClickHandler, + }, + }); + + return renderElement(); +} + +export default function ExampleText() { + return ( +

+ + Small size + + Medium size + + Large size + +
+ ); +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/index.ts similarity index 100% rename from docs/src/app/(public)/(content)/react/utils/use-renderer/demos/component/index.ts rename to docs/src/app/(public)/(content)/react/utils/use-renderer/demos/data-attributes/index.ts diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css new file mode 100644 index 0000000000..2b6986dfe2 --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css @@ -0,0 +1,10 @@ +.Text { + font-size: 0.875rem; + line-height: 1rem; + font-weight: 400; + color: var(--color-gray-900); +} + +strong.Text { + font-weight: 700; +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.tsx new file mode 100644 index 0000000000..0f7001dc59 --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.tsx @@ -0,0 +1,32 @@ +'use client'; +import * as React from 'react'; +import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; +import styles from './index.module.css'; + +type TextProps = { + className?: string; + render?: RenderProp>; + children: React.ReactNode; +}; + +function Text(props: TextProps) { + const { render, ...otherProps } = props; + + const { renderElement } = useRenderer({ + render: render ??

, + props: otherProps, + }); + + return renderElement(); +} + +export default function ExampleText() { + return ( +

+ Text component rendered as a paragraph tag + }> + Text component rendered as a strong tag + +
+ ); +} diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/index.ts similarity index 100% rename from docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/index.ts rename to docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/index.ts diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.module.css similarity index 54% rename from docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css rename to docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.module.css index db928e4cb2..c4786ca244 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.module.css @@ -4,19 +4,13 @@ line-height: 1rem; font-weight: 400; color: var(--color-gray-900); - &[data-size='small'] { + &[data-size-small] { font-size: 0.75rem; } - &[data-size='large'] { + &[data-size-large] { font-size: 1.25rem; } - &[data-weight='light'] { - font-weight: 300; - } - &[data-weight='bold'] { - font-weight: 700; - } - &[data-color='blue'] { + &[data-color-active] { color: var(--color-blue); } } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx similarity index 65% rename from docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx rename to docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx index d429ef3507..6570dac3b4 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/usage/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx @@ -3,19 +3,16 @@ import * as React from 'react'; import { useRenderer, RenderProp } from '@base-ui-components/react/use-renderer'; import styles from './index.module.css'; -type Weight = 'light' | 'regular' | 'bold'; type Size = 'small' | 'medium' | 'large'; -type Color = 'grey' | 'blue'; +type Color = 'default' | 'active'; type TextState = { - weight: Weight; size: Size; color: Color; }; type TextProps = { className: string | ((state: TextState) => string); - weight?: Weight; render?: RenderProp; onClick?: (event: React.MouseEvent) => void; children: React.ReactNode; @@ -23,30 +20,24 @@ type TextProps = { }; function Text(props: TextProps) { - const { - className, - render, - weight = 'regular', - size = 'medium', - onClick, - ...otherProps - } = props; - const [color, setColor] = React.useState('grey'); + const { className, render, size = 'medium', onClick, ...otherProps } = props; + const [color, setColor] = React.useState('default'); const onClickHandler = (event: React.MouseEvent) => { - setColor(color === 'grey' ? 'blue' : 'grey'); + setColor(color === 'default' ? 'active' : 'default'); onClick?.(event); }; - const state = React.useMemo( - () => ({ weight, size, color }), - [weight, size, color], - ); + const state = React.useMemo(() => ({ size, color }), [size, color]); const { renderElement } = useRenderer({ render: render ??

, state, className, + stateAttributesMap: { + size: (size) => ({ [`data-size-${size}`]: '' }), + color: (color) => ({ [`data-color-${color}`]: '' }), + }, props: { ...otherProps, onClick: onClickHandler, @@ -66,9 +57,6 @@ export default function ExampleText() { Large text - }> - Bold text rendered in a strong tag -

); } diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/index.ts b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/index.ts new file mode 100644 index 0000000000..94264ab85d --- /dev/null +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/index.ts @@ -0,0 +1,2 @@ +'use client'; +export { default as CssModules } from './css-modules'; diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index 32e7fcb1ae..f46384ff07 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -47,10 +47,14 @@ The hook returns a function that when called returns the element that should be ## Usage -This is an example usage for creating a Text component that generates data-attributes based on its state, and have the support for the `render` prop and the `className` callback. +This is an example of a Text component that provides the support for the render prop. - + -Alternatevly, you can use the Slot component +In the following demo, the Text component provides more Base UI features, like adding data attributes and `className` callback where developers can have access to the internal state of the component. - + + +Additionally, you can also customize how the data-attributes are generated, by providing the `stateAttributesMap` option. + + diff --git a/packages/react/src/use-renderer/useRenderer.ts b/packages/react/src/use-renderer/useRenderer.ts index 21ad78f73b..f0c5be5f6a 100644 --- a/packages/react/src/use-renderer/useRenderer.ts +++ b/packages/react/src/use-renderer/useRenderer.ts @@ -10,13 +10,13 @@ import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyle function useRenderer, RenderedElementType extends Element>( settings: useRenderer.Settings, ) { - const { className, render, state, props, stateAttributesMap } = settings; + const { className, render, state = {}, props, stateAttributesMap } = settings; const { ref, ...extraProps } = props ?? {}; return useComponentRenderer({ className, render, - state, + state: state as State, ref: ref as React.Ref, extraProps, propGetter: (x) => x, @@ -43,7 +43,7 @@ namespace useRenderer { /** * The state of the component. It will be used as a parameter for the render and className callbacks. */ - state: State; + state?: State; /** * Props to be spread on the rendered element. */ From a113201637e61d50907a8575836416d44ff1b0fb Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 13:16:05 +0100 Subject: [PATCH 28/31] remove slot component --- packages/react/package.json | 1 - packages/react/src/index.ts | 1 - packages/react/src/slot/Slot.tsx | 105 ------------------------------ packages/react/src/slot/index.tsx | 1 - 4 files changed, 108 deletions(-) delete mode 100644 packages/react/src/slot/Slot.tsx delete mode 100644 packages/react/src/slot/index.tsx diff --git a/packages/react/package.json b/packages/react/package.json index 59c6e27118..e99fe9bd98 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -49,7 +49,6 @@ "./select": "./src/select/index.ts", "./separator": "./src/separator/index.ts", "./slider": "./src/slider/index.ts", - "./slot": "./src/slot/index.ts", "./switch": "./src/switch/index.ts", "./tabs": "./src/tabs/index.ts", "./toggle": "./src/toggle/index.ts", diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 875f57278c..deabba51ee 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -19,7 +19,6 @@ export * from './radio-group'; export * from './scroll-area'; export * from './select'; export * from './separator'; -export * from './slot'; export * from './slider'; export * from './switch'; export * from './tabs'; diff --git a/packages/react/src/slot/Slot.tsx b/packages/react/src/slot/Slot.tsx deleted file mode 100644 index 99a8e52cbc..0000000000 --- a/packages/react/src/slot/Slot.tsx +++ /dev/null @@ -1,105 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { useComponentRenderer } from '../utils/useComponentRenderer'; -import { useForkRef } from '../utils'; -import type { BaseUIComponentProps } from '../utils/types'; -import type { ComponentRenderFn } from '../utils/types'; -import { defaultRenderFunctions } from '../utils/defaultRenderFunctions'; -import { CustomStyleHookMapping as StateDataAttributes } from '../utils/getStyleHookProps'; - -const Slot = React.forwardRef(function SlotComponent< - State extends Record, - RenderedElementType extends Element, ->( - inProps: Slot.Props, - forwardedRef: React.ForwardedRef, -) { - const { - className, - render =
, - state = {} as State, - stateAttributesMap, - ...props - } = inProps; - const { ref, ...extraProps } = props ?? {}; - const finalRef = useForkRef(ref, forwardedRef); - - const { renderElement } = useComponentRenderer({ - className, - render, - state, - ref: finalRef as React.Ref, - extraProps, - propGetter: (x) => x, - customStyleHookMapping: stateAttributesMap, - }); - - return renderElement(); -}); - -type RenderProp = - | ComponentRenderFn, State> - | React.ReactElement> - | keyof typeof defaultRenderFunctions; - -namespace Slot { - export interface Props, RenderedElementType extends Element> { - /** - * The class name to apply to the rendered element. - * Can be a string or a function that accepts the state and returns a string. - */ - className?: string | ((state: State) => string); - /** - * The render prop or React element to override the default element. - */ - render?: RenderProp; - /** - * The state of the component. It will be used as a parameter for the render and className callbacks. - */ - state?: State; - /** - * A mapping of state to data attributes. - */ - stateAttributesMap?: StateDataAttributes; - ref?: React.Ref; - } -} - -export { Slot }; - -Slot.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * @ignore - */ - children: PropTypes.node, - /** - * CSS class applied to the element, or a function that - * returns a class based on the component’s state. - */ - className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - /** - * The orientation of the separator. - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * Allows you to replace the component’s HTML element - * with a different tag, or compose it with another component. - * - * Accepts a `ReactElement` or a function that returns the element to render. - */ - render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), - /** - * The state of the component. It will be used as a parameter for the render and className callbacks. - */ - state: PropTypes.object, - /** - * Props to be spread on the rendered element. - */ - props: PropTypes.object, -} as any; diff --git a/packages/react/src/slot/index.tsx b/packages/react/src/slot/index.tsx deleted file mode 100644 index f521c503f4..0000000000 --- a/packages/react/src/slot/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Slot } from './Slot'; From be499fa2bdd318cbbdb28d1b73615a6e83e8c5f5 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 13:21:04 +0100 Subject: [PATCH 29/31] fix lint issue --- .../demos/state-attributes-map/css-modules/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx index 6570dac3b4..2c8c7708ab 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/state-attributes-map/css-modules/index.tsx @@ -35,8 +35,8 @@ function Text(props: TextProps) { state, className, stateAttributesMap: { - size: (size) => ({ [`data-size-${size}`]: '' }), - color: (color) => ({ [`data-color-${color}`]: '' }), + size: (value) => ({ [`data-size-${value}`]: '' }), + color: (value) => ({ [`data-color-${value}`]: '' }), }, props: { ...otherProps, From ed89bb18c54799a4c6775886df2aeee49d3578ac Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 13:29:36 +0100 Subject: [PATCH 30/31] remove non-sense styles --- .../use-renderer/demos/render/css-modules/index.module.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css index 2b6986dfe2..a9f3bad2d4 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/demos/render/css-modules/index.module.css @@ -1,10 +1,5 @@ .Text { font-size: 0.875rem; line-height: 1rem; - font-weight: 400; color: var(--color-gray-900); } - -strong.Text { - font-weight: 700; -} From a12e865bba9095c06a31c093cd2aa54c9ca416e4 Mon Sep 17 00:00:00 2001 From: mnajdova Date: Fri, 14 Feb 2025 13:46:57 +0100 Subject: [PATCH 31/31] markdownlint --- .../app/(public)/(content)/react/utils/use-renderer/page.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx index f46384ff07..b723241090 100644 --- a/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx +++ b/docs/src/app/(public)/(content)/react/utils/use-renderer/page.mdx @@ -18,12 +18,12 @@ The `useRenderer` hook allows you to support the same features Base UI provides className: { type: 'string | ((state: State) => string)', description: - 'CSS class applied to the element, or a function that returns a class based on the component’s state.', + "CSS class applied to the element, or a function that returns a class based on the component's state.", }, render: { type: 'RenderProp', description: - 'Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.', + "Allows you to replace the component's HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a \`ReactElement\` or a function that returns the element to render.", }, state: { type: 'State',