Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(theme): update config for extendBaseTheme #9681

Merged
merged 9 commits into from
Mar 27, 2023

Conversation

TylerAPfledderer
Copy link
Contributor

@TylerAPfledderer TylerAPfledderer commented Mar 9, 2023

Description

Applies Chakra's extendBaseTheme function for the custom config and updates the config to reflect this change.

  • The purpose of this function versus the previously used extendTheme is to not immediately include all of the default component themes.
  • Default themes are imported as needed to ensure the current UI is retained
  • A small reusable function is created to allow for recursive merging (deep nested merging) of default and custom styling to prevent any unnecessary overrides.

Certainly open for trimming any of this, either now or in a future PR.

Related Issue

Closes #9679

Comment on lines +4 to +29
const {
Accordion: accordionDefaultTheme,
Avatar: avatarDefaultTheme,
Badge: badgeDefaultTheme,
Breadcrumb: breadcrumbDefaultTheme,
Button: buttonDefaultTheme,
Checkbox: checkboxDefaultTheme,
CloseButton: closeButtonDefaultTheme,
Code: codeDefaultTheme,
Divider: dividerDefaultTheme,
Drawer: drawerDefaultTheme,
Form: formDefaultTheme,
FormLabel: formLabelDefaultTheme,
Heading: headingDefaultTheme,
Input: inputDefaultTheme,
Link: linkDefaultTheme,
List: listDefaultTheme,
Menu: menuDefaultTheme,
Modal: modalDefaultTheme,
Select: selectDefaultTheme,
Spinner: spinnerDefaultTheme,
Switch: switchDefaultTheme,
Table: tableDefaultTheme,
Tabs: tabsDefaultTheme,
Tag: tagDefaultTheme,
} = theme.components
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import aliases used for consistency, as some of these defaults are exported to custom component configs.

The rest are imported to retain the current UI with all Chakra components used that have themes, and includes themes for components that may be used later as the UI migration continues and other refactoring needs.

It is possible that this list could be trimmed down with any discovery of themes that are not actually needed.

Comment on lines +67 to +72
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
Copy link
Contributor Author

@TylerAPfledderer TylerAPfledderer Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function was created for re-usability to handle merging of default styles with custom stylings. It uses the lodash merge function to handle recursive merging to prevent unexpected overrides.

Though this works as intended, I believe this could be improved, as there are multiple circumstances that this would be used in.

Ex. 1: Simple merge

// From custom Link config
baseStyle: defineMergeStyles(linkDefaultTheme.baseStyle, {
    color: "primary",
    textDecoration: "underline",
    _focus: {
      boxShadow: "none",
    },
    _focusVisible: {
      boxShadow: "none",
      outline: "auto",
    },
  },
})

Ex. 2: Style function props needed to be passed to a default theme that uses some of those props (type error throws otherwise)

// From custom Button config
const variantOutline = defineStyle((props) =>
  defineMergeStyles(defaultVariants?.outline(props), commonOutline)
)

Ex. 3: There may be a circumstance where a default baseStyle would be combined with a default variant because the component may not use other variants in the project, thereby compacting the custom config.

const baseStyle = defineMergeStyles(
  defaultBaseStyle,
  defaultVariant.outline,
  {
    // Custom styles
  }
)

NOTE: There is a mergeThemeOverride that is available from Chakra that essentially does the same thing, but it doesn't have any type safety.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Would love to have this documented somewhere...I feel bad to lose this doc in this PR.

======
On the other hand, merge will return any, taking away the whole purpose of this function I believe. Shouldn't we do something like:
🤔

Suggested change
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs) as SystemStyleObject
}

or

Suggested change
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
export function defineMergeStyles<T extends SystemStyleObject>(
defaultTheming?: T,
...styleObjs: T[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs) as T
}

Copy link
Contributor Author

@TylerAPfledderer TylerAPfledderer Mar 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pettinarip

Awesome. Would love to have this documented somewhere...I feel bad to lose this doc in this PR.

I can put this up in the PR description as Additional Information 😃


With your second suggestion, this approach will return a type equivalent only to the first object, which would be the default theme, and therefore lock down what the second object could have in its properties.

With the first suggestion, this will throw a type error in configs where the function is used in a defineMultiStyleConfig property.

For example, the sizes property in the Button theme config. The error thrown here:

Type 'SystemStyleObject' is not assignable to type 'Dict<SystemStyleInterpolation> | undefined'

Dict is currently a private type in Chakra, but it's simply

type Dict<T = any> = {
  [ x: string ]: T
}

We could apply this assertion on the return, written as Record<string, SystemStyleObject> which will work everywhere.

export function defineMergeStyles(
  defaultTheming?: SystemStyleObject | unknown,
  ...styleObjs: SystemStyleObject[] | unknown[]
) {
  return merge(defaultTheming, ...styleObjs) as Record<string, SystemStyleObject>
}

Note that for both params, I am also using unknown for any object that is inferring as an object or undefined. (Type error throws otherwise because of either an object mismatch for defaultTheming or of the array type for styleObjs, if that makes sense)

@gatsby-cloud
Copy link

gatsby-cloud bot commented Mar 9, 2023

✅ ethereum-org-website-dev deploy preview ready

Copy link
Member

@pettinarip pettinarip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing @TylerAPfledderer!

A few minor observations but this looks really nice. GJ


const variantSecondary: SystemStyleObject = {
const { baseStyle: defaultBaseStyle, sizes: defaultSizes } = badgeDefaultTheme
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { baseStyle: defaultBaseStyle, sizes: defaultSizes } = badgeDefaultTheme
const { baseStyle: defaultBaseStyle } = badgeDefaultTheme


export const Button = defineStyleConfig({
baseStyle,
sizes: defineMergeStyles(defaultSizes, {
md: {
h: 42,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was my mistake, this should be

Suggested change
h: 42,
h: "42px",

const { baseStyle: defaultBaseStyle } = checkboxDefaultTheme

const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(checkboxAnatomy.keys)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, nice.

mt: "0.25rem",
},
},
alignTop: variantAlignTop,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
alignTop: variantAlignTop,
// TODO: remove this variant
alignTop: variantAlignTop,

@@ -1,46 +1,69 @@
import { StyleFunctionProps } from "@chakra-ui/react"
import type { ComponentStyleConfig } from "@chakra-ui/theme"
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"
import { createMultiStyleConfigHelpers } from "@chakra-ui/react"

Comment on lines +67 to +72
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Would love to have this documented somewhere...I feel bad to lose this doc in this PR.

======
On the other hand, merge will return any, taking away the whole purpose of this function I believe. Shouldn't we do something like:
🤔

Suggested change
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs) as SystemStyleObject
}

or

Suggested change
export function defineMergeStyles(
defaultTheming?: SystemStyleObject,
...styleObjs: SystemStyleObject[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs)
}
export function defineMergeStyles<T extends SystemStyleObject>(
defaultTheming?: T,
...styleObjs: T[] | undefined[]
) {
return merge(defaultTheming, ...styleObjs) as T
}

@@ -37,4 +49,8 @@ export const Tag: ComponentStyleConfig = {
},
custom: {}, // empty variant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
custom: {}, // empty variant
// TODO: remove this variant, as it doesn't exist on the DS
custom: {}, // empty variant

@pettinarip pettinarip requested a review from nhsz as a code owner March 24, 2023 20:28
@pettinarip
Copy link
Member

pettinarip commented Mar 27, 2023

@TylerAPfledderer

Not a big deal but I was analyzing the final bundles and noticed that the size had an unexpected result. They have basically the same size.

dev build (extendTheme):
@chakra-ui bundle size 277.01 kb
image

This PR build (extendBaseTheme):
@chakra-ui bundle size 286.39 kb
image

I was expecting a smaller size overall but we got an even slightly heavier one. Do you know what could be the reason? maybe is that we are not excluding a lot of Chakra components after all? 🤔

But again, not a big deal, regardless of the bundle size, I think this is a great improvement to reduce the number of theme styles we have.

@TylerAPfledderer
Copy link
Contributor Author

TylerAPfledderer commented Mar 27, 2023

@pettinarip I think it would still be hard to say at this point. It makes sense if the lack of excluded default component themes being imported, but I am expecting some trimming from the imports after starting work on PRs for the form elements, tags, tables, and avatars with their theming.

I would still find the results not telling when styled-components and the direct inclusion of emotion still a part of the package. Maybe it doesn't make a difference here!

I think what would be good for the Chakra team is to keep record of at least three bundle size points:

  • Bundle sizes before adding Chakra to the project
  • Bundle Size before the use of extendBaseTheme
  • Bundle Size after the removal of all the old theming imports from Styled-components and emotion

I'm not sure if this will make a difference, but what if indeed I allowed the rendering of ChakraBaseProvider what that would do to the bundle along with extendBaseTheme?

I've argued that this provider is a redundancy but would also be good to notate that result.

I wanted to save these bundle size notes for the case study so it's worth bringing this up 😁

@pettinarip
Copy link
Member

@TylerAPfledderer cool. Then, are you ok if I merge this PR? lgtm.

After that, you can play around with ChakraBaseProvider and see if that has any real impact. LMK what you think

@TylerAPfledderer
Copy link
Contributor Author

@pettinarip yes, go ahead and merge! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use extendBaseTheme instead of extendTheme
2 participants