refactor(ui): share one base props type across Mosaic components - #9267
Open
alexcarpenter wants to merge 14 commits into
Open
refactor(ui): share one base props type across Mosaic components#9267alexcarpenter wants to merge 14 commits into
alexcarpenter wants to merge 14 commits into
Conversation
🦋 Changeset detectedLatest commit: a934303 The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Move both typography components off the Emotion slot-recipe engine onto
StyleX atoms, following the Button migration's contract: a stable
`.cl-heading`/`.cl-text` class plus `data-size`/`data-intent` attrs, with
the consumer's `className`/`style` merged last so they win.
The size and intent axes are identical across the two components, so the
atoms live once in a shared `typography.styles.ts`; Heading only adds its
weight on top and Text adds nothing.
Collapse the `--cl-text-label-*` tokens into one named step scale
(`xs` -> `2xl`) that every text-bearing component sizes against -- the
`label`/`label-sm` steps were byte-identical to `sm`/`xs`. Font weights
become their own `--cl-font-{normal,medium,semibold,bold}` group, since
weight and size vary independently.
`render`, the Heading/Text contexts, and ref forwarding are unchanged.
The `sx` prop is dropped as it was for Button; the three call sites that
used it move to `style`.
…engine `PropTable` appended an `sx` row unconditionally, but StyleX components have no `sx` -- they merge the consumer's `className`/`style` after their own atoms. `meta.styleEngine` now picks the row set, and Button, Heading, and Text declare `'stylex'`.
Heading and Text take `color` (primary | neutral | warning | negative | positive) instead of `intent`, matching Badge and the palette tokens, and emit `data-color`. Badge moves onto the shared type scale now that the `--cl-text-label-*` tokens are gone.
`useRender` has always cloned a React element passed to `render`, and its own
doc comment advertises `render={<Link/>}`. The public `ComponentProps["render"]`
type only allowed the function form, so the element form was unreachable from
any primitive's props.
The two forms are not interchangeable: a render function is typed to receive the
tag's props verbatim, so it cannot render a component whose props diverge from
those of the tag. Widen the prop to the `RenderPropOrElement<Tag>` union the hook
already implements, and export it for consumers restating it inline.
`Heading` and `Text` type `color` as a variant, replacing the legacy HTML `color` attribute. A render function receives the tag's props verbatim, so spreading them into either component required destructuring `color` back out at every call site to drop a value Dialog never set — it was only present because `DialogTitleProps` extends `ComponentProps<"h2">`. The element form carries no such constraint, and was already the documented way to swap a Mosaic component's host element.
The StyleX migration left this weight as a literal `500`, since `Text` no longer accepts Emotion's `sx` and that is where `t.font.medium` came from. Read the migrated token instead, so a theme overriding `--cl-font-medium` still applies.
`appearance.icons` clones whatever element it is given; nothing requires an `svg`. The Icon tests override with a `span`.
alexcarpenter
force-pushed
the
carp/mosaic-typography-stylex
branch
from
July 28, 2026 19:49
71384a1 to
27b88a3
Compare
alexcarpenter
force-pushed
the
carp/mosaic-component-props
branch
from
July 28, 2026 19:50
aecb873 to
2f669ec
Compare
… in the type test
Heading, Text and Badge each derived their props differently: two base types (`React.ComponentPropsWithRef` vs headless `ComponentProps`), two things omitted (`color` vs `render`), the render union restated inline in one and imported in the other, and two ways of narrowing `color` — `Omit` then redeclare in the interfaces, silent intersection in the type alias. Collapse all of it into `MosaicComponentProps<Tag>`, mirroring Base UI's `BaseUIComponentProps`. `color` is omitted once, so the next component to expose it as a variant inherits the narrowing instead of rediscovering it. Type-only: all three components resolve to the same props as before.
alexcarpenter
force-pushed
the
carp/mosaic-component-props
branch
from
July 28, 2026 19:59
2f669ec to
a934303
Compare
maxyinger
approved these changes
Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Heading,TextandBadgeeach acceptrenderand each exposecoloras a variant, but all three derived their props differently: two base types (React.ComponentPropsWithRefvs headlessComponentProps), two different things omitted (colorvsrender), the render union restated inline in one and imported in the other, and two mechanisms for narrowingcolor—Omitthen redeclare in the interfaces, silent intersection in the type alias.This introduces one shared base in
mosaic/props.ts, mirroring Base UI'sBaseUIComponentProps:coloris omitted once rather than per component, so the next component to expose it as a variant inherits the narrowing instead of rediscovering the collision with the non-standard HTMLcolorattribute.Badgealso stops omittingrenderonly to re-add it.Type-only — all three components resolve to the same props as before, so no changeset body.
Stacked on
carp/mosaic-typography-stylex(#9244); merge that first.Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change