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

RFC: makeStyles() API changes #17076

Merged
merged 19 commits into from Mar 3, 2021
Merged

RFC: makeStyles() API changes #17076

merged 19 commits into from Mar 3, 2021

Conversation

layershifter
Copy link
Member

@layershifter layershifter commented Feb 19, 2021

This RFC is about proposed API changes to makeStyles(). RFC includes a description of potential performance issues and proposed design changes that should improve it.

HTML rendered version can be viewed here
A PR with implementation

@layershifter layershifter added the Type: RFC Request for Feedback label Feb 19, 2021
@layershifter layershifter changed the title RFC: makeStyles() replacement RFC: makeStyles() API changes Feb 19, 2021
@codesandbox-ci
Copy link

codesandbox-ci bot commented Feb 19, 2021

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 3744775:

Sandbox Source
Fluent UI Button Configuration
codesandbox-react-template Configuration
codesandbox-react-northstar-template Configuration

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
layershifter and others added 2 commits February 19, 2021 15:35
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
layershifter and others added 7 commits February 19, 2021 15:42
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
layershifter and others added 2 commits February 19, 2021 15:47
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
@layershifter layershifter marked this pull request as ready for review February 19, 2021 15:09
@size-auditor
Copy link

size-auditor bot commented Feb 19, 2021

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: a93b07b3154b68801090b70c4911f49ab25ecb12 (build)

@fabricteam
Copy link
Collaborator

fabricteam commented Feb 19, 2021

Perf Analysis

No significant results to display.

All results

Scenario Render type Master Ticks PR Ticks Iterations Status
Avatar mount 1276 1281 5000
BaseButton mount 971 993 5000
Breadcrumb mount 44553 43779 5000
ButtonNext mount 1369 1372 5000
Checkbox mount 1668 1634 5000
CheckboxBase mount 1362 1386 5000
ChoiceGroup mount 5165 5180 5000
ComboBox mount 1123 1055 1000
CommandBar mount 10486 10505 1000
ContextualMenu mount 6222 6345 1000
DefaultButton mount 1193 1198 5000
DetailsRow mount 3780 3787 5000
DetailsRowFast mount 3961 3762 5000
DetailsRowNoStyles mount 3724 3666 5000
Dialog mount 1586 1564 1000
DocumentCardTitle mount 1880 1882 1000
Dropdown mount 3539 3600 5000
FocusTrapZone mount 1878 1905 5000
FocusZone mount 1882 1884 5000
IconButton mount 1850 1916 5000
Label mount 349 358 5000
Layer mount 1918 1909 5000
Link mount 528 498 5000
MakeStyles mount 2116 2099 50000
MenuButton mount 1578 1608 5000
MessageBar mount 2114 2089 5000
Nav mount 3484 3516 1000
OverflowSet mount 1105 1088 5000
Panel mount 1450 1492 1000
Persona mount 848 852 1000
Pivot mount 1492 1495 1000
PrimaryButton mount 1360 1371 5000
Rating mount 7955 7950 5000
SearchBox mount 1419 1413 5000
Shimmer mount 2709 2741 5000
Slider mount 2054 2089 5000
SpinButton mount 5263 5228 5000
Spinner mount 441 427 5000
SplitButton mount 3381 3357 5000
Stack mount 529 532 5000
StackWithIntrinsicChildren mount 1614 1610 5000
StackWithTextChildren mount 4895 4929 5000
SwatchColorPicker mount 10658 10781 5000
Tabs mount 1464 1527 1000
TagPicker mount 2994 2962 5000
TeachingBubble mount 12161 12173 5000
Text mount 445 448 5000
TextField mount 1459 1478 5000
ThemeProvider mount 1214 1216 5000
ThemeProvider virtual-rerender 607 609 5000
ThemeProviderNext mount 15794 15706 5000
Toggle mount 862 859 5000
buttonNative mount 107 121 5000

Perf Analysis (Fluent)

Perf comparison
Status Scenario Fluent TPI Fabric TPI Ratio Iterations Ticks
🦄 Avatar.Fluent 0.2 0.5 0.4:1 2000 393
🦄 Button.Fluent 0.13 0.22 0.59:1 5000 674
🔧 Checkbox.Fluent 0.68 0.38 1.79:1 1000 675
🎯 Dialog.Fluent 0.17 0.22 0.77:1 5000 849
🔧 Dropdown.Fluent 3.08 0.42 7.33:1 1000 3080
🔧 Icon.Fluent 0.15 0.07 2.14:1 5000 751
🦄 Image.Fluent 0.09 0.13 0.69:1 5000 449
🔧 Slider.Fluent 1.61 0.47 3.43:1 1000 1606
🔧 Text.Fluent 0.08 0.03 2.67:1 5000 420
🦄 Tooltip.Fluent 0.12 0.9 0.13:1 5000 588

🔧 Needs work     🎯 On target     🦄 Amazing

Perf tests with no regressions
Scenario Current PR Ticks Baseline Ticks Ratio
SkeletonMinimalPerf.default 447 410 1.09:1
PortalMinimalPerf.default 174 161 1.08:1
ReactionMinimalPerf.default 478 450 1.06:1
TextAreaMinimalPerf.default 589 557 1.06:1
Button.Fluent 674 633 1.06:1
AvatarMinimalPerf.default 229 219 1.05:1
DialogMinimalPerf.default 875 830 1.05:1
CarouselMinimalPerf.default 546 525 1.04:1
ChatDuplicateMessagesPerf.default 407 392 1.04:1
FormMinimalPerf.default 501 481 1.04:1
LoaderMinimalPerf.default 797 770 1.04:1
MenuMinimalPerf.default 988 954 1.04:1
PopupMinimalPerf.default 768 735 1.04:1
Text.Fluent 420 402 1.04:1
ButtonUseCssNestingPerf.default 1149 1117 1.03:1
DropdownManyItemsPerf.default 826 805 1.03:1
HeaderMinimalPerf.default 437 425 1.03:1
TableMinimalPerf.default 478 464 1.03:1
Avatar.Fluent 393 382 1.03:1
AccordionMinimalPerf.default 173 169 1.02:1
DividerMinimalPerf.default 442 435 1.02:1
DropdownMinimalPerf.default 3188 3112 1.02:1
GridMinimalPerf.default 413 404 1.02:1
ListWith60ListItems.default 704 687 1.02:1
RadioGroupMinimalPerf.default 510 498 1.02:1
TextMinimalPerf.default 413 405 1.02:1
ToolbarMinimalPerf.default 1083 1062 1.02:1
AnimationMinimalPerf.default 442 436 1.01:1
BoxMinimalPerf.default 417 411 1.01:1
CardMinimalPerf.default 639 630 1.01:1
EmbedMinimalPerf.default 4417 4394 1.01:1
InputMinimalPerf.default 1363 1347 1.01:1
ItemLayoutMinimalPerf.default 1361 1350 1.01:1
LayoutMinimalPerf.default 456 452 1.01:1
ListMinimalPerf.default 555 549 1.01:1
SegmentMinimalPerf.default 413 407 1.01:1
IconMinimalPerf.default 754 748 1.01:1
TooltipMinimalPerf.default 873 862 1.01:1
VideoMinimalPerf.default 702 694 1.01:1
Icon.Fluent 751 740 1.01:1
AttachmentSlotsPerf.default 1314 1315 1:1
ButtonMinimalPerf.default 204 203 1:1
ButtonOverridesMissPerf.default 1747 1749 1:1
ChatMinimalPerf.default 683 684 1:1
CheckboxMinimalPerf.default 2962 2962 1:1
DatepickerMinimalPerf.default 48593 48594 1:1
ImageMinimalPerf.default 439 441 1:1
LabelMinimalPerf.default 478 478 1:1
ListNestedPerf.default 644 647 1:1
SliderMinimalPerf.default 1586 1587 1:1
SplitButtonMinimalPerf.default 3995 3991 1:1
TableManyItemsPerf.default 2277 2277 1:1
CustomToolbarPrototype.default 3742 3756 1:1
Checkbox.Fluent 675 672 1:1
Dialog.Fluent 849 846 1:1
Image.Fluent 449 450 1:1
AlertMinimalPerf.default 327 331 0.99:1
ButtonSlotsPerf.default 630 639 0.99:1
FlexMinimalPerf.default 348 350 0.99:1
HeaderSlotsPerf.default 890 895 0.99:1
ProviderMergeThemesPerf.default 1590 1606 0.99:1
RefMinimalPerf.default 244 247 0.99:1
StatusMinimalPerf.default 796 804 0.99:1
Dropdown.Fluent 3080 3116 0.99:1
Slider.Fluent 1606 1619 0.99:1
Tooltip.Fluent 588 594 0.99:1
ChatWithPopoverPerf.default 455 463 0.98:1
ListCommonPerf.default 727 740 0.98:1
MenuButtonMinimalPerf.default 1710 1742 0.98:1
ProviderMinimalPerf.default 959 988 0.97:1
TreeMinimalPerf.default 861 884 0.97:1
AttachmentMinimalPerf.default 180 187 0.96:1
ButtonUseCssPerf.default 850 882 0.96:1
RosterPerf.default 1247 1314 0.95:1
TreeWith60ListItems.default 178 190 0.94:1

rootShape20: { width: '20px', height: '20px' },
rootShape24: { width: '24px', height: '24px' },
rootShape28: { width: '28px', height: '28px' },
})
Copy link
Collaborator

@JustSlone JustSlone Feb 19, 2021

Choose a reason for hiding this comment

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

[non-blocking] I do quite like this pattern of defining styles ahead of time, then conditionally applying them below. We should measure, but my intuition says that this will have good opportunity for optimization and gets us closer to static styles.

I also find this more intuitive than the matcher approach 👍 I think this will be easier for devs to understand and other teams to use in their products.

Copy link
Member Author

Choose a reason for hiding this comment

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

I also find this more intuitive than the matcher approach 👍 I think this will be easier for devs to understand and other teams to use in their products.

There is one interesting difference:

  • with matchers (i.e. makeStyles()) you can just execute a function and get classes back
  • with makeOverrides() you need to execute component's code to understand applied styles

I am considering this an implementation difference that should not block us from moving forward.

Copy link
Member

@khmakoto khmakoto Feb 24, 2021

Choose a reason for hiding this comment

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

Question: What prevents us from abstracting the component code into a separate function so that we can, like you said, just execute a function and get classes back?

Copy link
Member Author

Choose a reason for hiding this comment

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

Question: What prevents us from abstracting the component code into a separate function so that we can, like you said, just execute a function and get classes back?

function getRootClasses(state, classes) {
  return ax(classes.root, state.primary && classes.primary, /* etc. */)
}

Do you mean something like this?

@soettl
Copy link
Member

soettl commented Feb 23, 2021

I think it is awesome that we are hyper-focused on performance. My team cannot adopt the new theming system (and as a result, Fluent UI components) unless the performance is very close to CSS modules. From this perspective, I like this updated spec.

In the spec, can you add an example that shows how one can specify selectors like :focus, :hover and others?

@soettl
Copy link
Member

soettl commented Feb 23, 2021

In this new styling/theming system, what is the recommended way for customers to customize the styling of Fluent components?

```tsx
import { ax, makeOverrides } from "@fluentui/react-make-styles";

const useStyles = makeStyles({
Copy link
Member

Choose a reason for hiding this comment

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

I can't find it in the spec anymore, but when I last looked at it, the theming object was passed to each individual 'slot'. Can it just be an argument to a callback for makeOverrides first argument?

I.e.

const useStyles = makeOverrides((theme: ITheme) => {
  root: ...,
  other: ...
}));

instead of

const useStyles = makeOverrides({
  root: (theme: ITheme) => { ... },
  other: (theme: ITheme) => { ... }
});

Saves some lambda creation at runtime and in my opinion is a bit more intuitive.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can it just be an argument to a callback for makeOverrides first argument?

We are considering this option, but there are few open questions.

1. IE11 should have decent performance if there will be a decision to include it into supported browsers, in this case build step can split styles to runtime dependent:

// ⚠️ not real code, just shows an idea
const useStyles = makeStyles([
  [null, theme => ({ color: "red", background: theme.colors.red })]
]);
// =>

const useStyles = makeStyles([
  [null, { color: "red" }],
  [null, theme => ({ background: theme.colors.red })] // only "theme" usages are inside this closure
]);

2. Our work related to tokens is not finished yet, we may decide to go with CSS variables (and some magic shim for IE11, if required). But, the open question is why we should have theme and closures at all?

const useStyles = makeStyles([
  // current option, simpler implementation for IE11
  [null, theme => ({ background: theme.colors.red })],
  // "vars" can be an object will all CSS variables that are in theme to enforce type safety
  [null, { background: vars.colors.red }],
  // can we just use CSS variables?
  [null, { background: "--theme-colors-red" }]
]);

Saves some lambda creation at runtime and in my opinion is a bit more intuitive.

Once we will have build time transforms, there will be no closures for modern browsers at all in runtime 🐱 Currently this work is done on an initial render, for example:

const useLabelStyles = makeStyles<AvatarState>([
  [
    null,
    tokens => ({
      color: tokens.alias.color.neutral.neutralForeground3,
      background: tokens.alias.color.neutral.neutralBackground6
    })
  ]
]);

Will produce this CSS:

CSS variables output

@layershifter
Copy link
Member Author

layershifter commented Feb 24, 2021

In the spec, can you add an example that shows how one can specify selectors like :focus, :hover and others?

To clarify: proposed API changes will not affect syntax support in any way. The goal is to eliminate "matchers level" that does not work so well as it was expected 😭

An original makeStyles.md spec includes an example of selectors usage in "Basic algorithm", but it's not clear agree 👍 We will have more detailed docs in Storybook and make-styles/react-make-styles READMEs soon, it's an action item.

Currently following selectors are supported (we will include these as an examples to docs 🐱 ):

const useStyles = makeStyles([
  [
    null,
    {
      ":hover": { color: "red" }, // and other pseudo states
      ".classname": { color: "red" },
      "& .classname": { color: "red" },
      "& .classname > div": { color: "red" },
      ":global(body)": { color: "red" } // :global is also supported
    }
  ]
]);

@layershifter
Copy link
Member Author

In this new styling/theming system, what is the recommended way for customers to customize the styling of Fluent components?

⚠️ It's still under construction, it's a way where are going, but it's not ready yet for mass adoption. I don't recommend to use it in production until Fluent UI team will come with a release announcement.

Copy link
Member

@khmakoto khmakoto left a comment

Choose a reason for hiding this comment

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

Really love this, thanks for putting this out!

Comment on lines 176 to 177
// 👎 It might be tricky find proper names to express definition names
// (we can end with "rootPrimaryCirclularGhostEtc.")
Copy link
Member

Choose a reason for hiding this comment

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

Not blocking by any means for this PR but we might want to find a convention for how we name things.

For example, we could go with [slot] [alphabetically ordered states].

So, for the example above that would be rootCircularGhostPrimary.

Just food for thought 😄.

rfcs/convergence/make-overrides.md Outdated Show resolved Hide resolved
rootShape20: { width: '20px', height: '20px' },
rootShape24: { width: '24px', height: '24px' },
rootShape28: { width: '28px', height: '28px' },
})
Copy link
Member

@khmakoto khmakoto Feb 24, 2021

Choose a reason for hiding this comment

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

Question: What prevents us from abstracting the component code into a separate function so that we can, like you said, just execute a function and get classes back?

Co-authored-by: Makoto Morimoto <humbertomakotomorimoto@gmail.com>
Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>
@layershifter layershifter merged commit fb5f4df into master Mar 3, 2021
@layershifter layershifter deleted the layershifter-patch-1 branch March 3, 2021 11:58
joshualamusga1 pushed a commit to joshualamusga1/fluentui that referenced this pull request Mar 25, 2021
* RFC: makeStyles() replacement

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

* Update rfcs/convergence/make-overrides.md

* Update make-overrides.md

* Create make-overrides.md

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: Makoto Morimoto <humbertomakotomorimoto@gmail.com>

* Update rfcs/convergence/make-overrides.md

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>

* update formatting

Co-authored-by: ling1726 <lingfan.gao@microsoft.com>
Co-authored-by: Makoto Morimoto <humbertomakotomorimoto@gmail.com>
Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: RFC Request for Feedback
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants