From f952302dabdaf081a62140dec5b15550cc31b261 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 3 Jan 2023 18:08:29 +0100 Subject: [PATCH 01/26] RFC: Styles handbook --- rfcs/react-components/styles-handbook.md | 771 +++++++++++++++++++++++ 1 file changed, 771 insertions(+) create mode 100644 rfcs/react-components/styles-handbook.md diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md new file mode 100644 index 0000000000000..e76e2dbd5373c --- /dev/null +++ b/rfcs/react-components/styles-handbook.md @@ -0,0 +1,771 @@ +# Styles Handbook + +[@layeshifter](https://github.com/layershifter) + +This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used in Fluent UI React v9) to style components. + +## Table of contents + + + + +- [Introduction](#introduction) +- [Basics](#basics) +- [APIs](#apis) + - [`makeStyles`](#makestyles) + - [Limitations](#limitations) + - [`mergeClasses()`](#mergeclasses) + - [Do not concatenate classes](#do-not-concatenate-classes) + - [`makeResetStyles`](#makeresetstyles) + - [Hybrid approach](#hybrid-approach) +- [Advanced](#advanced) + - [Understanding selectors complexity](#understanding-selectors-complexity) +- [Best practices](#best-practices) + - [Writting styles](#writting-styles) + - [Use `tokens` over direct colors](#use-tokens-over-direct-colors) + - [Avoid duplication in rule definitions](#avoid-duplication-in-rule-definitions) + - [Avoid `!important`](#avoid-important) + - [Performance](#performance) + - [Avoid nested `mergeClasses` calls](#avoid-nested-mergeclasses-calls) + - [Nested selectors](#nested-selectors) + - [Usage with pseudo classes](#usage-with-pseudo-classes) + - [Do not use selectors to target elements](#do-not-use-selectors-to-target-elements) + - [Do not introduce complicated selectors](#do-not-introduce-complicated-selectors) + - [Avoid usage of input pseudo classes](#avoid-usage-of-input-pseudo-classes) + - [RTL styles](#rtl-styles) + - [Using `@noflip`](#using-noflip) + + + +# Introduction + +Griffel is a hybrid CSS-in-JS that features runtime option like any other CSS-in-JS and [Ahead-of-time compilation][griffel-aot] with [CSS extraction][griffel-css-extraction] to reduce runtime footprint and improve performance. + +# Basics + +Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value is written as a single CSS rule. + +```css +/* Monolithic classes */ +/* Can be applied only to a specific button */ +.button { + display: flex; + align-items: center; +} + +/* Atomic CSS */ +/* Can be applied to any element that needs there rules */ +.display-flex { + display: flex; +} +.align-items-center { + align-items: center; +} +``` + +[Learn more][griffel-atomic-css] about Atomic CSS. + +# APIs + +## `makeStyles` + +Is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: + +```jsx +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + button: { display: 'flex' }, + icon: { paddingLeft: '5px' }, +}); +``` + +### Limitations + +Atomic CSS generates more classes than the approach with monolithic classes. Usually it's not a problem, but there are cases when performance [may degrade][griffel-recalc-performance], for example when elements have more than 100 classes. This does not happen usually as there is a limited set of CSS properties that is applied to a DOM element: + +```js +import { makeStyles, shorthands } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + display: 'flex', + alignItems: 'center', + ...shorthands.padding('4px'), + }, +}); +// ⬇️⬇️⬇️ +// produces 6 classes (1+1+4) +``` + +> 💡**Tip:** To check generated classes consider to use [Try out][griffel-try-out] sandbox. + +However, the situation can go out of control with nested selectors, [pseudo classes][mdn-pseudo-classes]/selectors and [At-rules][mdn-at-rules] causing "CSS rules explosion": + +```js +import { makeStyles, shorthands } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + ...shorthands.padding('4px'), + ...shorthands.margin('4px'), + + ':hover': { + ...shorthands.padding('4px'), + ...shorthands.margin('4px'), + }, + + '::before': { + display: 'block', + content: "' '", + }, + + '@media (forced-colors: active)': { + ...shorthands.padding('4px'), + ...shorthands.margin('4px'), + }, + }, +}); +// ⬇️⬇️⬇️ +// produces 14 classes ((4+4)+(4+4)+2+(4+4)) +``` + +Such cases might be unavoidable by design, next section covers APIs to address this problem. + +## `mergeClasses()` + +Griffel provides [`mergeClasses() API`][griffel-merge-classes] that is expected to be used in cases with styles permutations: + +```js +import { makeStyles, shorthands } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + /* styles */ + }, + foo: { + /* styles */ + }, + bar: { + /* styles */ + }, +}); + +function Component(props) { + const classes = useClasses(); + const className = mergeClasses( + classes.root, + props.foo && classes.foo /* styles specific for "foo" */, + props.bar && classes.bar /* styles specific for "bar" */, + ); + + /* --- */ +} +``` + +### Do not concatenate classes + +It is not possible to simply concatenate classes returned by `useClasses()` hooks. Always use `mergeClasses()` to merge classes as results of concatenation can contain duplicated classes and lead to non-deterministic results. + +```js +import { makeStyles, mergeClasses } from '@griffel/react'; + +const useClasses = makeStyles({ + rootA: { display: 'flex' }, + rootB: { display: 'grid' }, +}); + +function App(props) { + const classes = useClasses(); + + // ✅ Returns "class-display-grid" + const correctClasses = mergeClasses(classes.rootA, classes.rootB); + // 🔴 Never concatenate class strings, returns "class-display-flex class-display-grid" + const wrongClasses = classes.rootA + ' ' + classes.rootB; +} +``` + +## `makeResetStyles` + +Is used to define base styles in components avoid the "CSS rules explosion" problem as generates a monolithic class: + +```js +import { makeResetStyles } from '@griffel/react'; + +const useBaseClassname = makeResetStyles({ + padding: '4px', + margin: '4px', +}); +``` + +> ⚠️ **Note:** Only one class generated by `makeResetStyles()` can be applied to an element. Otherwise, behavior will be non-deterministic as classes merging will not be done for this case and results depend on order of insertion. + +### Hybrid approach + +Our recommendation is use [`makeResetStyles` API][griffel-make-reset-styles] to define base styles for a component and keep using `makeStyles` for overrides and permutations: + +```jsx +import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; + +const useBaseClassname = makeResetStyles({ + ':hover': { + ...shorthands.padding('4px'), + /* other styles */ + }, + '::before': { + display: 'block', + content: "' '", + }, + '@media (forced-colors: active)': { + ...shorthands.padding('4px'), + /* other styles */ + }, +}); +// ⬇️⬇️⬇️ +// produces 1 class + +const useClasses = makeStyles({ + circular: { + ...shorthands.borderRadius('10px'), + }, + primary: { + color: 'pink', + }, +}); +// ⬇️⬇️⬇️ +// produces 4/1 classes to be conditionally applied + +function Component(props) { + const baseClassName = useBaseClassname(); + const classes = useClasses(); + + const className = mergeClasses(baseClassName, props.circular && classes.circular, props.primary && classes.primary); + + return
; +} +``` + +# Advanced + +## Understanding selectors complexity + +CSS Selectors are matched by browser engines from [right to left (bottom-up parsing)][stackoverflow-selectors-match]: + +```mermaid +flowchart RL + C[li] --> B[ul] --> A[.menu] +``` + +```css +.menu ul li { + color: #00f; +} +``` + +> The browser first checks for `li`, then `ul`, and then `.menu`. + +It means that we need to understand selectors that we are writing and avoid _wide_ selectors. A component below will be used as an example: + +```jsx +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + test: { + color: 'orange', + + '> *': { + color: 'red', + }, + '> h1': { + color: 'magenta', + }, + '> div': { + color: 'green', + }, + '> .ui-button': { + color: 'blue', + }, + }, +}); + +function App() { + const classes = useClasses(); + + return ( + <> +
+

Hello World

+ +
+ + {/* Renders 500 empty divs */} + {Array.from({ length: 500 }, (_, i) => ( +
+ ))} + + ); +} +``` + +#### Measurements + +##### No selector (best) + +Matches only an element where is applied. + +- selector `.fe3e8s9` +- match_attempts **1** +- match_count **1** + +##### `> *` (worst) + +"\*" matches any element on a page. + +- selector `.fzbuleu > *` +- match_attempts **502** +- match_count **1** + +##### `> h1` & `> div` (non ideal) + +Targets all `h1` & `div` tags. + +- selector `.fohk16y > h1` +- match_attempts **1** +- match_count **1** + +In this example performs better as page has a single `h1` tag, this will not be a case in the real app that it is visible on the next example with `div`. + +- selector `.fq4d7o6 > div` +- match_attempts **501** +- match_count **1** + +#### Targeting a classname (better) + +The most effective out there as we target only elements with a specific classname. However, it will not save from the case with 1k buttons in the app. + +- selector `.fqhvij7 > .ui-button` +- match_attempts **1** +- match_count **1** + +# Best practices + +## Writting styles + +### Use `tokens` over direct colors + +Fluent UI React v9 provides [design tokens][fluent-colors] to provide consistent way of theming via `tokens` provided by `@fluentui/react-theme`. + +```js +import { makeStyles } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + // ❌ Don't do + rootA: { color: 'red' /* brand foreground */ }, + // ✅ Do + rootB: { color: tokens.colorBrandForeground1 }, +}); +``` + +Exact colors should be never used in Fluent UI code and are not recommended by be used in applications. The exception are [system-colors][mdn-system-colors] used by forced colors mode: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + button: { color: 'ButtonText' }, +}); +``` + +### Avoid duplication in rule definitions + +Styles written for components should follow these rules: + +- base styles should contain the most of CSS definitions that are applicable to base state +- styles for permutations should be granular and don't contain definitions that are present in base styles + +```js +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + base: { + display: 'flex', + color: tokens.colorNeutralForeground1, + ...shorthands.padding('10px'), + }, + // ❌ Don't do + // "display" & "padding" with the same values are defined in base styles + primary: { + display: 'flex', + ...shorthands.padding('10px'), + backgroundColor: tokens.colorBrandBackground, + color: tokens.colorBrandForeground1, + }, + // ✅ Do + primary: { + backgroundColor: tokens.colorBrandBackground, + color: tokens.colorBrandForeground1, + }, +}); + +function App(props) { + const classes = useClasses(); + const className = mergeClasses(classes.base, props.primary && classes.primary); + + /* --- */ +} +``` + +### Avoid `!important` + +Our styles are written in way to allow predictable and simple style overrides, `!important` should not be used in styles: + +```js +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; + +const useClasses = makeStyles({ + base: { + // ❌ Don't do + display: 'flex !important', + }, +}); +``` + +## Performance + +### Avoid nested `mergeClasses` calls + +`mergeClasses` is a performant function, however it's not expected that it will be called multiple times for the same element. + +```js +// ❌ Don't do +function Component(props) { + const classes = useClasses(); + + const classesForFoo = mergeClasses(/* ---- */); + const className = mergeClasses(classes.root, classesForFoo, mergeClasses(/* ---- */), mergeClasses(/* ---- */)); + + /* --- */ +} +``` + +Conditions to apply styles might be complex, in this case consider to extract them to separate variables: + +```js +// ✅ Do +function Component(props) { + const classes = useClasses(); + + const conditionForFoo = /* ---- */ true; + const className = mergeClasses(classes.root, conditionForFoo && classes.foo /* other condition */); + + /* --- */ +} +``` + +## Nested selectors + +Nested selectors in styles should be used responsibly as they can cause "CSS rules explosion" problem. + +### Usage with pseudo classes + +Use nested selectors when they are combined with pseudo classes: + +```js +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + base: { + // ✅ Do + // Shows filled icon on hover + ':hover': { + [`& .${iconFilledClassName}`]: { + display: 'inline', + }, + [`& .${iconRegularClassName}`]: { + display: 'none', + }, + }, + }, +}); +``` + +### Do not use selectors to target elements + +Do not use nested selectors to target slots or nested elements that are accessible directly: + +```jsx +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + root: { + backgroundColor: tokens.colorNeutralBackground1, + // ❌ Don't do + // You can apply classes directly to that "div" + '> div': { + color: tokens.colorNeutralForeground1, + }, + }, + + // ✅ Do + root: { + backgroundColor: tokens.colorNeutralBackground1, + }, + slot: { + color: tokens.colorNeutralForeground1, + }, +}); + +function App(props) { + const classes = useClasses(); + + return ( +
+ {/* 💡 Classes can be passed directly to this element */} +
+
+ ); +} +``` + +### Do not introduce complicated selectors + +Keep selectors simple to produce reusable CSS rules: + +- CSS rules that are unique cannot be reused in other areas + ```css + /* ⬇️ cannot be reused in other components */ + .hash .some-unique-class { + display: flex; + } + ``` +- Complicated selectors produce bigger bundle size with AOT + + ```js + makeStyles({ + rootA: { + display: 'flex', + }, + rootB: { + '> .some-classname': { + '> .other-classname': { + display: 'flex', + alignItems: 'center', + }, + }, + }, + }); + ``` + + ⬇️⬇️⬇️ + + ```css + /* ✅ no selectors */ + .f22iagw { + display: flex; + } + + /* ⚠️ with complex selectors */ + .f1312jvm > .some-classname > .other-classname { + display: flex; + } + .f1c58nry > .some-classname > .other-classname { + align-items: center; + } + ``` + +```js +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + root: { + // ❌ Don't do + // Avoid complex selectors i.e. simplify them + '> .foo-classname': { + '> .bar-classname': { + '> .baz-classname': { + display: 'flex', + alignItems: 'center', + }, + }, + }, + }, + + // ✅ Do + // Apply classes directly to an element + baz: { + display: 'flex', + alignItems: 'center', + }, +}); +``` + +### Avoid usage of input pseudo classes + +Instead of usage [input pseudo classes][mdn-input-pseudo-classes] in styles, prefer to use JS state. + +- Produces less classes on an element +- Selectors for overrides are simpler +- These pseudo classes are supported only on input elements + +```jsx +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { tokens } from '@fluentui/react-theme'; + +const useClasses = makeStyles({ + root: { + color: tokens.colorNeutralForeground1, + // ❌ Don't do + ':checked': { + color: tokens.colorNeutralForeground2, + }, + }, + + // ✅ Do + root: { + color: tokens.colorNeutralForeground1, + }, + checked: { + color: tokens.colorNeutralForeground2, + }, +}); + +function Checkbox(props) { + const [checked, setChecked] = React.useState(); + const classes = useClasses(); + + return ; +} +``` + +## RTL styles + +Griffel performs automatic flipping of properties and values in Right-To-Left (RTL) text direction. + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + paddingLeft: '10px', + }, +}); +``` + +⬇️⬇️⬇️ + +```css +/* Will be applied in LTR */ +.frdkuqy { + padding-left: 10px; +} +/* Will be applied in RTL */ +.f81rol6 { + padding-right: 10px; +} +``` + +Values than contain CSS variables (or our `tokens`) might not be always converted, for example: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + // ⚠️ "boxShadow" will not be flipped in this example + boxShadow: 'var(--box-shadow)', + }, +}); +``` + +In this case, please define own styles and get a direction from `useFluent()` hook: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + boxShadow: 'var(--box-shadow)', + }, + rtl: { + boxShadow: 'var(--box-shadow-in-rtl)', + }, +}); + +function App() { + const classes = useClasses(); + const { dir } = useFluent(); + const className = mergeClasses(classes.root, dir === 'rtl' && classes.rtl); + + /* --- */ +} +``` + +### Using `@noflip` + +You can also control which rules you don't want to flip by adding a `/* @noflip */` CSS comment to your rule: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + paddingLeft: '10px /* @noflip */', + }, +}); +``` + +⬇️⬇️⬇️ + +```css +/* Will be applied in LTR & RTL */ +.f6x5cb6 { + padding-left: 10px; +} +``` + +This is useful to define direction specific animations: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + ltr: { + animationName: { + '0%': { left: '0% /* @noflip */' }, + '100%': { left: '100% /* @noflip */' }, + }, + }, + rtl: { + animationName: { + '100%': { right: '-100% /* @noflip */' }, + '0%': { right: '100% /* @noflip */' }, + }, + }, +}); +``` + +In this case automatic flipping is disabled and will produce less CSS: 2 classes instead of 4. + +[fluent-colors]: https://react.fluentui.dev/?path=/docs/theme-color--page +[griffel]: https://griffel.js.org +[griffel-aot]: https://griffel.js.org/react/ahead-of-time-compilation/introduction +[griffel-atomic-css]: https://griffel.js.org/react/guides/atomic-css +[griffel-css-extraction]: https://griffel.js.org/react/css-extraction/introduction +[griffel-make-reset-styles]: https://griffel.js.org/react/api/make-reset-styles +[griffel-merge-classes]: https://griffel.js.org/react/api/merge-classes +[griffel-recalc-performance]: https://griffel.js.org/react/guides/atomic-css#recalculation-performance +[griffel-try-out]: https://griffel.js.org/try-it-out/ +[mdn-at-rules]: https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule +[mdn-match-media]: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia +[mdn-pseudo-classes]: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes +[mdn-input-pseudo-classes]: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes#input_pseudo-classes +[mdn-system-colors]: https://developer.mozilla.org/en-US/docs/Web/CSS/system-color +[react-hook]: https://reactjs.org/docs/hooks-intro.html +[stackoverflow-selectors-match]: https://stackoverflow.com/a/5813672/6488546 +[youtube-atomic-css-scale]: https://youtu.be/9JZHodNR184?t=764 +[youtube-css-specificity]: https://youtu.be/a8TFywbXBt0?t=104 From 80f28026377e6dc0559ef1462eb38a271c72c8ea Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:17:06 +0100 Subject: [PATCH 02/26] Apply suggestions from code review Co-authored-by: Miroslav Stastny Co-authored-by: ling1726 --- rfcs/react-components/styles-handbook.md | 63 ++++++++++++------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index e76e2dbd5373c..abb9acba1f352 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -54,7 +54,7 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value } /* Atomic CSS */ -/* Can be applied to any element that needs there rules */ +/* Can be applied to any element that needs these rules */ .display-flex { display: flex; } @@ -69,7 +69,7 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value ## `makeStyles` -Is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: +`makeStyles` is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: ```jsx import { makeStyles } from '@griffel/react'; @@ -82,7 +82,7 @@ const useClasses = makeStyles({ ### Limitations -Atomic CSS generates more classes than the approach with monolithic classes. Usually it's not a problem, but there are cases when performance [may degrade][griffel-recalc-performance], for example when elements have more than 100 classes. This does not happen usually as there is a limited set of CSS properties that is applied to a DOM element: +Atomic CSS generates more classes than a standard approach with monolithic classes. Usually it's not a problem, but there are cases when performance [may degrade][griffel-recalc-performance], for example when elements have more than 100 classes. This does not happen usually as there are only a limited number of CSS properties that can be applied to a DOM element: ```js import { makeStyles, shorthands } from '@griffel/react'; @@ -98,9 +98,9 @@ const useClasses = makeStyles({ // produces 6 classes (1+1+4) ``` -> 💡**Tip:** To check generated classes consider to use [Try out][griffel-try-out] sandbox. +> 💡**Tip:** Preview generated classes consider in the [Try out][griffel-try-out] sandbox. -However, the situation can go out of control with nested selectors, [pseudo classes][mdn-pseudo-classes]/selectors and [At-rules][mdn-at-rules] causing "CSS rules explosion": +However, "CSS rule explosion" can happen when using nested selectors, [pseudo classes][mdn-pseudo-classes]/selectors and [At-rules][mdn-at-rules]: ```js import { makeStyles, shorthands } from '@griffel/react'; @@ -134,7 +134,7 @@ Such cases might be unavoidable by design, next section covers APIs to address t ## `mergeClasses()` -Griffel provides [`mergeClasses() API`][griffel-merge-classes] that is expected to be used in cases with styles permutations: +The [`mergeClasses() API`][griffel-merge-classes] should be used when multiple Griffel styles are used on the same element. ```js import { makeStyles, shorthands } from '@griffel/react'; @@ -163,7 +163,7 @@ function Component(props) { } ``` -### Do not concatenate classes +### ⚠️ Only combine classes with `mergeClasses` It is not possible to simply concatenate classes returned by `useClasses()` hooks. Always use `mergeClasses()` to merge classes as results of concatenation can contain duplicated classes and lead to non-deterministic results. @@ -187,7 +187,7 @@ function App(props) { ## `makeResetStyles` -Is used to define base styles in components avoid the "CSS rules explosion" problem as generates a monolithic class: +This API works similarly to `makeStyles` and is used to styles as a single monolithic class to avoid the "CSS rules explosion" problem. ```js import { makeResetStyles } from '@griffel/react'; @@ -198,11 +198,11 @@ const useBaseClassname = makeResetStyles({ }); ``` -> ⚠️ **Note:** Only one class generated by `makeResetStyles()` can be applied to an element. Otherwise, behavior will be non-deterministic as classes merging will not be done for this case and results depend on order of insertion. +> ⚠️ **Note:** Only one class generated by `makeResetStyles()` can be applied to an element. Otherwise, behavior will be non-deterministic since styles are not merged and deduplicated, the results will depend on order of insertion. -### Hybrid approach +### Hybrid approach (Using `makeStyles` and `makeResetStyles` together) -Our recommendation is use [`makeResetStyles` API][griffel-make-reset-styles] to define base styles for a component and keep using `makeStyles` for overrides and permutations: +We recommend using [`makeResetStyles` API][griffel-make-reset-styles] to define the base styles for a component and use `makeStyles` to override or enhance the base styles at runtime: ```jsx import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -247,7 +247,7 @@ function Component(props) { # Advanced -## Understanding selectors complexity +## Understanding selector complexity CSS Selectors are matched by browser engines from [right to left (bottom-up parsing)][stackoverflow-selectors-match]: @@ -264,7 +264,7 @@ flowchart RL > The browser first checks for `li`, then `ul`, and then `.menu`. -It means that we need to understand selectors that we are writing and avoid _wide_ selectors. A component below will be used as an example: +It means that we need to understand the selectors that we are writing and avoid _wide_ selectors. Let's use the component below as an example: ```jsx import { makeStyles } from '@griffel/react'; @@ -311,7 +311,7 @@ function App() { ##### No selector (best) -Matches only an element where is applied. +Only matches the element that the class is applied to. - selector `.fe3e8s9` - match_attempts **1** @@ -319,7 +319,7 @@ Matches only an element where is applied. ##### `> *` (worst) -"\*" matches any element on a page. +"\*" matches all elements on the page. - selector `.fzbuleu > *` - match_attempts **502** @@ -341,7 +341,7 @@ In this example performs better as page has a single `h1` tag, this will not be #### Targeting a classname (better) -The most effective out there as we target only elements with a specific classname. However, it will not save from the case with 1k buttons in the app. +The most effective since we only target elements with a specific class. However, this is still not optimal if there are 1000 elements with the class `ui-button` on the page. - selector `.fqhvij7 > .ui-button` - match_attempts **1** @@ -353,7 +353,7 @@ The most effective out there as we target only elements with a specific classnam ### Use `tokens` over direct colors -Fluent UI React v9 provides [design tokens][fluent-colors] to provide consistent way of theming via `tokens` provided by `@fluentui/react-theme`. +Fluent UI React v9 provides [design tokens][fluent-colors] for consistent theming. ```js import { makeStyles } from '@griffel/react'; @@ -367,7 +367,7 @@ const useClasses = makeStyles({ }); ``` -Exact colors should be never used in Fluent UI code and are not recommended by be used in applications. The exception are [system-colors][mdn-system-colors] used by forced colors mode: +Exact colors should be never used in Fluent UI code and and we do not recommend their use in applications either. The exception are [system-colors][mdn-system-colors] used by forced colors mode: ```js import { makeStyles } from '@griffel/react'; @@ -377,12 +377,13 @@ const useClasses = makeStyles({ }); ``` -### Avoid duplication in rule definitions +### Avoid rule duplication Styles written for components should follow these rules: - base styles should contain the most of CSS definitions that are applicable to base state -- styles for permutations should be granular and don't contain definitions that are present in base styles +- styles for permutations should be granular +- base styles should not be duplicated by permutations ```js import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -419,7 +420,7 @@ function App(props) { ### Avoid `!important` -Our styles are written in way to allow predictable and simple style overrides, `!important` should not be used in styles: +Our styles are written in way to allow predictable and simple style overrides, `!important` should not be necessary to override any styles: ```js import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -434,7 +435,7 @@ const useClasses = makeStyles({ ## Performance -### Avoid nested `mergeClasses` calls +### Use `mergeClasses` once for an element `mergeClasses` is a performant function, however it's not expected that it will be called multiple times for the same element. @@ -466,9 +467,9 @@ function Component(props) { ## Nested selectors -Nested selectors in styles should be used responsibly as they can cause "CSS rules explosion" problem. +Use nested selectors responsibly in styles because they can cause "CSS rule explosion". -### Usage with pseudo classes +### Use nested selectors with pseudo classes Use nested selectors when they are combined with pseudo classes: @@ -492,9 +493,9 @@ const useClasses = makeStyles({ }); ``` -### Do not use selectors to target elements +### Apply classes directly to elements -Do not use nested selectors to target slots or nested elements that are accessible directly: +Do not use nested selectors to target an element or slot if you can apply classes on directly on it. ```jsx import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -531,7 +532,7 @@ function App(props) { } ``` -### Do not introduce complicated selectors +### Do not use complicated selectors Keep selectors simple to produce reusable CSS rules: @@ -604,13 +605,13 @@ const useClasses = makeStyles({ }); ``` -### Avoid usage of input pseudo classes +### Avoid input pseudo classes Instead of usage [input pseudo classes][mdn-input-pseudo-classes] in styles, prefer to use JS state. - Produces less classes on an element - Selectors for overrides are simpler -- These pseudo classes are supported only on input elements +- These pseudo classes are supported by input elements ```jsx import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -682,7 +683,7 @@ const useClasses = makeStyles({ }); ``` -In this case, please define own styles and get a direction from `useFluent()` hook: +In this case, please apply your own styles with the text direction from the `useFluent()` hook: ```js import { makeStyles } from '@griffel/react'; @@ -728,7 +729,7 @@ const useClasses = makeStyles({ } ``` -This is useful to define direction specific animations: +This can be useful to define direction specific animations: ```js import { makeStyles } from '@griffel/react'; From 3c10c6a82940090ad3cbb8147d616f5045d0df64 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:30:22 +0100 Subject: [PATCH 03/26] add a note about imports --- rfcs/react-components/styles-handbook.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index abb9acba1f352..69239b670ce14 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -15,23 +15,23 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [`makeStyles`](#makestyles) - [Limitations](#limitations) - [`mergeClasses()`](#mergeclasses) - - [Do not concatenate classes](#do-not-concatenate-classes) + - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) - [`makeResetStyles`](#makeresetstyles) - - [Hybrid approach](#hybrid-approach) + - [Hybrid approach (Using `makeStyles` and `makeResetStyles` together)](#hybrid-approach-using-makestyles-and-makeresetstyles-together) - [Advanced](#advanced) - - [Understanding selectors complexity](#understanding-selectors-complexity) + - [Understanding selector complexity](#understanding-selector-complexity) - [Best practices](#best-practices) - [Writting styles](#writting-styles) - [Use `tokens` over direct colors](#use-tokens-over-direct-colors) - - [Avoid duplication in rule definitions](#avoid-duplication-in-rule-definitions) + - [Avoid rule duplication](#avoid-rule-duplication) - [Avoid `!important`](#avoid-important) - [Performance](#performance) - - [Avoid nested `mergeClasses` calls](#avoid-nested-mergeclasses-calls) + - [Use `mergeClasses` once for an element](#use-mergeclasses-once-for-an-element) - [Nested selectors](#nested-selectors) - - [Usage with pseudo classes](#usage-with-pseudo-classes) - - [Do not use selectors to target elements](#do-not-use-selectors-to-target-elements) - - [Do not introduce complicated selectors](#do-not-introduce-complicated-selectors) - - [Avoid usage of input pseudo classes](#avoid-usage-of-input-pseudo-classes) + - [Use nested selectors with pseudo classes](#use-nested-selectors-with-pseudo-classes) + - [Apply classes directly to elements](#apply-classes-directly-to-elements) + - [Do not use complicated selectors](#do-not-use-complicated-selectors) + - [Avoid input pseudo classes](#avoid-input-pseudo-classes) - [RTL styles](#rtl-styles) - [Using `@noflip`](#using-noflip) @@ -67,6 +67,8 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value # APIs +> ⚠️ **Note:** All examples in this document use `@griffel/react` package. However, if you're a Fluent UI consumer please use `@fluentui/react-components` in imports. + ## `makeStyles` `makeStyles` is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: From dbe5fd5cda1e998bb4b24754b1a67005493ba347 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:40:55 +0100 Subject: [PATCH 04/26] rename to "Performance caveat" --- rfcs/react-components/styles-handbook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 69239b670ce14..59ce114c65a37 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -13,7 +13,7 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Basics](#basics) - [APIs](#apis) - [`makeStyles`](#makestyles) - - [Limitations](#limitations) + - [Performance caveat](#performance-caveat) - [`mergeClasses()`](#mergeclasses) - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) - [`makeResetStyles`](#makeresetstyles) @@ -82,7 +82,7 @@ const useClasses = makeStyles({ }); ``` -### Limitations +### Performance caveat Atomic CSS generates more classes than a standard approach with monolithic classes. Usually it's not a problem, but there are cases when performance [may degrade][griffel-recalc-performance], for example when elements have more than 100 classes. This does not happen usually as there are only a limited number of CSS properties that can be applied to a DOM element: From fff73393dc9dd67109f65b50890575745de7c6d9 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:51:17 +0100 Subject: [PATCH 05/26] add note about shorthands --- rfcs/react-components/styles-handbook.md | 45 ++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 59ce114c65a37..6861b9ace8013 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -13,6 +13,7 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Basics](#basics) - [APIs](#apis) - [`makeStyles`](#makestyles) + - [Limitations](#limitations) - [Performance caveat](#performance-caveat) - [`mergeClasses()`](#mergeclasses) - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) @@ -67,13 +68,13 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value # APIs -> ⚠️ **Note:** All examples in this document use `@griffel/react` package. However, if you're a Fluent UI consumer please use `@fluentui/react-components` in imports. +> 💡 **Note:** All examples in this document use `@griffel/react` package. However, if you're a Fluent UI consumer please use `@fluentui/react-components` in imports. ## `makeStyles` `makeStyles` is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: -```jsx +```js import { makeStyles } from '@griffel/react'; const useClasses = makeStyles({ @@ -82,6 +83,44 @@ const useClasses = makeStyles({ }); ``` +### Limitations + +`makeStyles()` does not support [CSS shorthands][griffel-css-shorthands-support] in styles definitions. However, Griffel provides a set of [`shorthands` functions][griffel-css-shorthands] to mimic them: + +```js +import { makeStyles, shorthands } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + // ❌ This is not supported, TypeScript compiler will throw, styles will not be inserted to DOM + padding: '2px 4px 8px 16px', + // ✅ Use shorthand functions to avoid writting CSS longhands + ...shorthands.padding('2px', '4px', '8px', '16px'), + }, +}); +``` + +> 💡 **Note:** The most of the functions follow syntax in matching CSS properties, but each value should a separate argument: + +```js +// ❌ Will produce wrong results: +// { +// paddingBottom: "2px 4px" +// paddingLeft: "2px 4px" +// paddingRight: "2px 4px" +// paddingTop: "2px 4px" +// } +shorthands.padding('2px 4px'); +// ✅ Correct output: +// { +// paddingBottom: "2px" +// paddingLeft: "4px" +// paddingRight: "4px" +// paddingTop: "2px" +// } +shorthands.padding('2px', '4px'); +``` + ### Performance caveat Atomic CSS generates more classes than a standard approach with monolithic classes. Usually it's not a problem, but there are cases when performance [may degrade][griffel-recalc-performance], for example when elements have more than 100 classes. This does not happen usually as there are only a limited number of CSS properties that can be applied to a DOM element: @@ -759,6 +798,8 @@ In this case automatic flipping is disabled and will produce less CSS: 2 classes [griffel-aot]: https://griffel.js.org/react/ahead-of-time-compilation/introduction [griffel-atomic-css]: https://griffel.js.org/react/guides/atomic-css [griffel-css-extraction]: https://griffel.js.org/react/css-extraction/introduction +[griffel-css-shorthands]: https://griffel.js.org/react/api/shorthands +[griffel-css-shorthands-support]: https://griffel.js.org/react/guides/limitations/#css-shorthands-are-not-supported [griffel-make-reset-styles]: https://griffel.js.org/react/api/make-reset-styles [griffel-merge-classes]: https://griffel.js.org/react/api/merge-classes [griffel-recalc-performance]: https://griffel.js.org/react/guides/atomic-css#recalculation-performance From cb1d84c8b7d76aa7b51c025c07626ab6a7e0a703 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:55:08 +0100 Subject: [PATCH 06/26] Apply suggestions from code review Co-authored-by: Miroslav Stastny --- rfcs/react-components/styles-handbook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 6861b9ace8013..82a360899cb8e 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -136,7 +136,7 @@ const useClasses = makeStyles({ }, }); // ⬇️⬇️⬇️ -// produces 6 classes (1+1+4) +// produces 6 classes (1 + 1 + 4 for expanded padding) ``` > 💡**Tip:** Preview generated classes consider in the [Try out][griffel-try-out] sandbox. @@ -168,7 +168,7 @@ const useClasses = makeStyles({ }, }); // ⬇️⬇️⬇️ -// produces 14 classes ((4+4)+(4+4)+2+(4+4)) +// produces 26 classes ((4+4)+(4+4)+2+(4+4)) ``` Such cases might be unavoidable by design, next section covers APIs to address this problem. From fdbbb10e7e55af647458e1f247101d5cff5d27bf Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 12:57:12 +0100 Subject: [PATCH 07/26] add a link to makeResetStyles section --- rfcs/react-components/styles-handbook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 82a360899cb8e..51874aebb8648 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -171,7 +171,7 @@ const useClasses = makeStyles({ // produces 26 classes ((4+4)+(4+4)+2+(4+4)) ``` -Such cases might be unavoidable by design, next section covers APIs to address this problem. +Such cases might be unavoidable by design, [the next section](#makeresetstyles) covers APIs to address this problem. ## `mergeClasses()` From a6b2c35ce7898f3dd8924ad16d2562cfdd64c1da Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 13:03:54 +0100 Subject: [PATCH 08/26] update structure --- rfcs/react-components/styles-handbook.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 51874aebb8648..6fa795b5d29ea 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -10,8 +10,8 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Introduction](#introduction) -- [Basics](#basics) -- [APIs](#apis) +- [Core concepts](#core-concepts) + - [Atomic CSS](#atomic-css) - [`makeStyles`](#makestyles) - [Limitations](#limitations) - [Performance caveat](#performance-caveat) @@ -19,7 +19,6 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) - [`makeResetStyles`](#makeresetstyles) - [Hybrid approach (Using `makeStyles` and `makeResetStyles` together)](#hybrid-approach-using-makestyles-and-makeresetstyles-together) -- [Advanced](#advanced) - [Understanding selector complexity](#understanding-selector-complexity) - [Best practices](#best-practices) - [Writting styles](#writting-styles) @@ -42,7 +41,9 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i Griffel is a hybrid CSS-in-JS that features runtime option like any other CSS-in-JS and [Ahead-of-time compilation][griffel-aot] with [CSS extraction][griffel-css-extraction] to reduce runtime footprint and improve performance. -# Basics +# Core concepts + +## Atomic CSS Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value is written as a single CSS rule. @@ -66,8 +67,6 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value [Learn more][griffel-atomic-css] about Atomic CSS. -# APIs - > 💡 **Note:** All examples in this document use `@griffel/react` package. However, if you're a Fluent UI consumer please use `@fluentui/react-components` in imports. ## `makeStyles` @@ -286,8 +285,6 @@ function Component(props) { } ``` -# Advanced - ## Understanding selector complexity CSS Selectors are matched by browser engines from [right to left (bottom-up parsing)][stackoverflow-selectors-match]: @@ -348,8 +345,6 @@ function App() { } ``` -#### Measurements - ##### No selector (best) Only matches the element that the class is applied to. From 1e9f83d46da26f1f38bd35179808df8312c3c3bb Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 13:09:33 +0100 Subject: [PATCH 09/26] Apply suggestions from code review Co-authored-by: ling1726 --- rfcs/react-components/styles-handbook.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 6fa795b5d29ea..f7954b2d6eba6 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -369,7 +369,7 @@ Targets all `h1` & `div` tags. - match_attempts **1** - match_count **1** -In this example performs better as page has a single `h1` tag, this will not be a case in the real app that it is visible on the next example with `div`. +The performance in this example is acceptable because the page only has a single `h1` tag, this will not be a case in a real app. The performance problem is more obvious here with the `div` selector. - selector `.fq4d7o6 > div` - match_attempts **501** @@ -418,6 +418,7 @@ const useClasses = makeStyles({ Styles written for components should follow these rules: - base styles should contain the most of CSS definitions that are applicable to base state + - use `makeResetStyles` to define them - styles for permutations should be granular - base styles should not be duplicated by permutations From 82fe5be9f01918bc55392601be924ab0536209b6 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 13:12:05 +0100 Subject: [PATCH 10/26] update system colors part --- rfcs/react-components/styles-handbook.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index f7954b2d6eba6..06e58d50af4fa 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -403,13 +403,17 @@ const useClasses = makeStyles({ }); ``` -Exact colors should be never used in Fluent UI code and and we do not recommend their use in applications either. The exception are [system-colors][mdn-system-colors] used by forced colors mode: +Exact colors should be never used in Fluent UI code and we do not recommend their use in applications either. The exception are [system-colors][mdn-system-colors] used by forced colors mode (inside media queries): ```js import { makeStyles } from '@griffel/react'; const useClasses = makeStyles({ - button: { color: 'ButtonText' }, + button: { + '@media (forced-colors: active)': { + color: 'ButtonText', + }, + }, }); ``` From 87a9eaa5637c8e931473abdf45eb796acd48013a Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 15:19:18 +0100 Subject: [PATCH 11/26] fix stats --- rfcs/react-components/styles-handbook.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 06e58d50af4fa..b61193c68bed1 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -358,8 +358,8 @@ Only matches the element that the class is applied to. "\*" matches all elements on the page. - selector `.fzbuleu > *` -- match_attempts **502** -- match_count **1** +- match_attempts **503** +- match_count **2** ##### `> h1` & `> div` (non ideal) @@ -385,7 +385,7 @@ The most effective since we only target elements with a specific class. However, # Best practices -## Writting styles +## Writing styles ### Use `tokens` over direct colors From 80ff7caeb426ee6e9da9734f8a7e57f51dd4c102 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 15:23:47 +0100 Subject: [PATCH 12/26] highlight the important phrase --- rfcs/react-components/styles-handbook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index b61193c68bed1..539f0104d1af4 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -369,7 +369,7 @@ Targets all `h1` & `div` tags. - match_attempts **1** - match_count **1** -The performance in this example is acceptable because the page only has a single `h1` tag, this will not be a case in a real app. The performance problem is more obvious here with the `div` selector. +The performance in this example is acceptable because the page only has a single `h1` tag, **this will not be a case in a real app**. The performance problem is more obvious here with the `div` selector. - selector `.fq4d7o6 > div` - match_attempts **501** From 9e9d2e4cbfe9092f031add9da68e558c8388fc16 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 15:30:52 +0100 Subject: [PATCH 13/26] use makeResetStyles in examples --- rfcs/react-components/styles-handbook.md | 89 ++++++++++++------------ 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 539f0104d1af4..cd4f8df80256b 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -427,15 +427,15 @@ Styles written for components should follow these rules: - base styles should not be duplicated by permutations ```js -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; +const useBaseClassName = makeResetStyles({ + display: 'flex', + color: tokens.colorNeutralForeground1, + padding: '10px', +}); const useClasses = makeStyles({ - base: { - display: 'flex', - color: tokens.colorNeutralForeground1, - ...shorthands.padding('10px'), - }, // ❌ Don't do // "display" & "padding" with the same values are defined in base styles primary: { @@ -452,8 +452,9 @@ const useClasses = makeStyles({ }); function App(props) { + const baseClassName = useBaseClassName(); const classes = useClasses(); - const className = mergeClasses(classes.base, props.primary && classes.primary); + const className = mergeClasses(baseClassName, props.primary && classes.primary); /* --- */ } @@ -483,10 +484,11 @@ const useClasses = makeStyles({ ```js // ❌ Don't do function Component(props) { + const baseClassName = useBaseClassName(); const classes = useClasses(); const classesForFoo = mergeClasses(/* ---- */); - const className = mergeClasses(classes.root, classesForFoo, mergeClasses(/* ---- */), mergeClasses(/* ---- */)); + const className = mergeClasses(baseClassName, classesForFoo, mergeClasses(/* ---- */), mergeClasses(/* ---- */)); /* --- */ } @@ -497,10 +499,11 @@ Conditions to apply styles might be complex, in this case consider to extract th ```js // ✅ Do function Component(props) { + const baseClassName = useBaseClassName(); const classes = useClasses(); const conditionForFoo = /* ---- */ true; - const className = mergeClasses(classes.root, conditionForFoo && classes.foo /* other condition */); + const className = mergeClasses(baseClassName, conditionForFoo && classes.foo /* other condition */); /* --- */ } @@ -515,10 +518,10 @@ Use nested selectors responsibly in styles because they can cause "CSS rule expl Use nested selectors when they are combined with pseudo classes: ```js -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; -const useClasses = makeStyles({ +const useBaseClassName = makeResetStyles({ base: { // ✅ Do // Shows filled icon on hover @@ -543,7 +546,7 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; const useClasses = makeStyles({ - root: { + slotA: { backgroundColor: tokens.colorNeutralBackground1, // ❌ Don't do // You can apply classes directly to that "div" @@ -553,10 +556,10 @@ const useClasses = makeStyles({ }, // ✅ Do - root: { + slotA: { backgroundColor: tokens.colorNeutralBackground1, }, - slot: { + slotB: { color: tokens.colorNeutralForeground1, }, }); @@ -620,29 +623,27 @@ Keep selectors simple to produce reusable CSS rules: ``` ```js -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; -const useClasses = makeStyles({ - root: { - // ❌ Don't do - // Avoid complex selectors i.e. simplify them - '> .foo-classname': { - '> .bar-classname': { - '> .baz-classname': { - display: 'flex', - alignItems: 'center', - }, +// ❌ Don't do +// Avoid complex selectors i.e. simplify them +const useBaseClassName = makeResetStyles({ + '> .foo-classname': { + '> .bar-classname': { + '> .baz-classname': { + display: 'flex', + alignItems: 'center', }, }, }, +}); - // ✅ Do - // Apply classes directly to an element - baz: { - display: 'flex', - alignItems: 'center', - }, +// ✅ Do +// Apply classes directly to an element +const useBaseBazClasses = makeResetStyles({ + display: 'flex', + alignItems: 'center', }); ``` @@ -658,19 +659,19 @@ Instead of usage [input pseudo classes][mdn-input-pseudo-classes] in styles, pre import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; -const useClasses = makeStyles({ - root: { - color: tokens.colorNeutralForeground1, - // ❌ Don't do - ':checked': { - color: tokens.colorNeutralForeground2, - }, +// ❌ Don't do +const useBaseClassName = makeResetStyles({ + color: tokens.colorNeutralForeground1, + ':checked': { + color: tokens.colorNeutralForeground2, }, +}); - // ✅ Do - root: { - color: tokens.colorNeutralForeground1, - }, +// ✅ Do +const useBaseClassName = makeResetStyles({ + color: tokens.colorNeutralForeground1, +}); +const useClasses = makeStyles({ checked: { color: tokens.colorNeutralForeground2, }, @@ -678,9 +679,11 @@ const useClasses = makeStyles({ function Checkbox(props) { const [checked, setChecked] = React.useState(); + + const baseClassName = useBaseClassName(); const classes = useClasses(); - return ; + return ; } ``` From 18de44fce73fab82cd6d4940bdfae473b5fe1641 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 15:36:00 +0100 Subject: [PATCH 14/26] add notes about overrides --- rfcs/react-components/styles-handbook.md | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index cd4f8df80256b..83e2c0ebd699b 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -622,6 +622,31 @@ Keep selectors simple to produce reusable CSS rules: } ``` +- Complicated selectors are hard to override as overrides should match component's styles + + ```js + // On component side + makeResetStyles({ + '> .some-classname': { + '> .other-classname': { + ':hover': { + display: 'flex', + alignItems: 'center', + }, + }, + }, + }); + // On consumer side 💥 + makeStyles({ + foo: { + '> .some-classname > .other-classname:hover': { + display: 'flex', + alignItems: 'center', + }, + }, + }); + ``` + ```js import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; From e15b91b34ebdfff01637f0a8088db38de37e04b6 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 15:51:49 +0100 Subject: [PATCH 15/26] add order of mergeClasses to basic --- rfcs/react-components/styles-handbook.md | 27 +++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 83e2c0ebd699b..2328d590fa76b 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -16,12 +16,13 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Limitations](#limitations) - [Performance caveat](#performance-caveat) - [`mergeClasses()`](#mergeclasses) + - [Order of arguments determines results](#order-of-arguments-determines-results) - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) - [`makeResetStyles`](#makeresetstyles) - [Hybrid approach (Using `makeStyles` and `makeResetStyles` together)](#hybrid-approach-using-makestyles-and-makeresetstyles-together) - [Understanding selector complexity](#understanding-selector-complexity) - [Best practices](#best-practices) - - [Writting styles](#writting-styles) + - [Writing styles](#writing-styles) - [Use `tokens` over direct colors](#use-tokens-over-direct-colors) - [Avoid rule duplication](#avoid-rule-duplication) - [Avoid `!important`](#avoid-important) @@ -203,6 +204,30 @@ function Component(props) { } ``` +### Order of arguments determines results + +Unlike native CSS, the output of mergeClasses() is affected by the order of the classes passed in, allowing for control over priority of style overrides. + +```js +import { mergeClasses, makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + blue: { color: 'blue' }, + red: { color: 'red' }, +}); + +function Component(props) { + // ℹ️ Order of arguments determines the results + + const redClassName = mergeClasses(classes.blue, classes.red); + // 👆 { color: 'red' } + const blueClassName = mergeClasses(classes.red, classes.blue); + // 👆 { color: 'blue' } + + /* --- */ +} +``` + ### ⚠️ Only combine classes with `mergeClasses` It is not possible to simply concatenate classes returned by `useClasses()` hooks. Always use `mergeClasses()` to merge classes as results of concatenation can contain duplicated classes and lead to non-deterministic results. From ddbdb922a9f201c29b22f71adc9d58039fd2092a Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 17:15:56 +0100 Subject: [PATCH 16/26] split selectors cases, add "Use structured styles" --- rfcs/react-components/styles-handbook.md | 62 +++++++++++++++++++++--- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 2328d590fa76b..eb4d896906faf 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -26,12 +26,13 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Use `tokens` over direct colors](#use-tokens-over-direct-colors) - [Avoid rule duplication](#avoid-rule-duplication) - [Avoid `!important`](#avoid-important) + - [Use structured styles](#use-structured-styles) - [Performance](#performance) - [Use `mergeClasses` once for an element](#use-mergeclasses-once-for-an-element) - [Nested selectors](#nested-selectors) - [Use nested selectors with pseudo classes](#use-nested-selectors-with-pseudo-classes) - [Apply classes directly to elements](#apply-classes-directly-to-elements) - - [Do not use complicated selectors](#do-not-use-complicated-selectors) + - [Avoid complicated selectors](#avoid-complicated-selectors) - [Avoid input pseudo classes](#avoid-input-pseudo-classes) - [RTL styles](#rtl-styles) - [Using `@noflip`](#using-noflip) @@ -386,15 +387,19 @@ Only matches the element that the class is applied to. - match_attempts **503** - match_count **2** -##### `> h1` & `> div` (non ideal) +##### `> h1` (non ideal) -Targets all `h1` & `div` tags. +Targets all `h1` tags on page. - selector `.fohk16y > h1` - match_attempts **1** - match_count **1** -The performance in this example is acceptable because the page only has a single `h1` tag, **this will not be a case in a real app**. The performance problem is more obvious here with the `div` selector. +The performance in this example is acceptable because the page only has a single `h1` tag, **this will not be a case in a real app**. + +##### `> div` (non ideal) + +The selector is similar to the previous case, but now it targets all `div` tags. All the example contains 501 `div`, the performance problem is more obvious here. - selector `.fq4d7o6 > div` - match_attempts **501** @@ -402,12 +407,28 @@ The performance in this example is acceptable because the page only has a single #### Targeting a classname (better) -The most effective since we only target elements with a specific class. However, this is still not optimal if there are 1000 elements with the class `ui-button` on the page. +This kind of selectors may not be the most efficient for pages with 1000 elements of the class 'ui-button', but it is the most effective as it targets only elements with that specific class. - selector `.fqhvij7 > .ui-button` - match_attempts **1** - match_count **1** +It is recommended to use this method in situations where pseudo selectors or pseudo classes are used, for example: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + test: { + ':hover': { + '> .ui-button': { + color: 'blue', + }, + }, + }, +}); +``` + # Best practices ## Writing styles @@ -500,6 +521,35 @@ const useClasses = makeStyles({ }); ``` +### Use structured styles + +To make your code simpler, consider grouping styles that have similar conditions in `mergeClasses()` calls, and then apply them without the use of additional conditions at all. + +```js +import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/react'; + +const useBaseClassName = makeResetStyles({ + display: 'flex', + fontSize: '16px', +}); +const useClasses = makeStyles({ + small: { fontSize: '12px' }, + medium: { + /* defined in base styles */ + }, + large: { fontSize: '20px' }, +}); + +function Component(props) { + const baseClassName = useBaseClassName(); + const classes = useClasses(); + + const className = mergeClasses(baseClassName, classes[props.size]); + + /* --- */ +} +``` + ## Performance ### Use `mergeClasses` once for an element @@ -601,7 +651,7 @@ function App(props) { } ``` -### Do not use complicated selectors +### Avoid complicated selectors Keep selectors simple to produce reusable CSS rules: From 734c7ebac531eaebcab624b2fa0da6acc2a27ffb Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 16 Jan 2023 17:19:38 +0100 Subject: [PATCH 17/26] split and move RTL parts --- rfcs/react-components/styles-handbook.md | 225 ++++++++++++----------- 1 file changed, 114 insertions(+), 111 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index eb4d896906faf..179cce37d7e02 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -20,6 +20,8 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [⚠️ Only combine classes with `mergeClasses`](#%EF%B8%8F-only-combine-classes-with-mergeclasses) - [`makeResetStyles`](#makeresetstyles) - [Hybrid approach (Using `makeStyles` and `makeResetStyles` together)](#hybrid-approach-using-makestyles-and-makeresetstyles-together) + - [RTL styles](#rtl-styles) + - [CSS variables caveat](#css-variables-caveat) - [Understanding selector complexity](#understanding-selector-complexity) - [Best practices](#best-practices) - [Writing styles](#writing-styles) @@ -29,13 +31,12 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i - [Use structured styles](#use-structured-styles) - [Performance](#performance) - [Use `mergeClasses` once for an element](#use-mergeclasses-once-for-an-element) + - [Avoid unnecessary RTL transforms by using `@noflip`](#avoid-unnecessary-rtl-transforms-by-using-noflip) - [Nested selectors](#nested-selectors) - [Use nested selectors with pseudo classes](#use-nested-selectors-with-pseudo-classes) - [Apply classes directly to elements](#apply-classes-directly-to-elements) - [Avoid complicated selectors](#avoid-complicated-selectors) - [Avoid input pseudo classes](#avoid-input-pseudo-classes) - - [RTL styles](#rtl-styles) - - [Using `@noflip`](#using-noflip) @@ -311,6 +312,71 @@ function Component(props) { } ``` +## RTL styles + +`makeStyles` & `makeResetStyles` perform automatic flipping of properties and values in Right-To-Left (RTL) text direction. + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + paddingLeft: '10px', + }, +}); +``` + +⬇️⬇️⬇️ + +```css +/* Will be applied in LTR */ +.frdkuqy { + padding-left: 10px; +} +/* Will be applied in RTL */ +.f81rol6 { + padding-right: 10px; +} +``` + +### CSS variables caveat + +Values than contain CSS variables (or our `tokens`) might not be always converted, for example: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + // ⚠️ "boxShadow" will not be flipped in this example + boxShadow: 'var(--box-shadow)', + }, +}); +``` + +In this case, please apply your own styles with the text direction from the `useFluent()` hook: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + boxShadow: 'var(--box-shadow)', + }, + rtl: { + boxShadow: 'var(--box-shadow-in-rtl)', + }, +}); + +function App() { + const classes = useClasses(); + const { dir } = useFluent(); + const className = mergeClasses(classes.root, dir === 'rtl' && classes.rtl); + + /* --- */ +} +``` + ## Understanding selector complexity CSS Selectors are matched by browser engines from [right to left (bottom-up parsing)][stackoverflow-selectors-match]: @@ -584,6 +650,52 @@ function Component(props) { } ``` +### Avoid unnecessary RTL transforms by using `@noflip` + +You can also control which rules you don't want to flip by adding a `/* @noflip */` CSS comment to your rule: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + paddingLeft: '10px /* @noflip */', + }, +}); +``` + +⬇️⬇️⬇️ + +```css +/* Will be applied in LTR & RTL */ +.f6x5cb6 { + padding-left: 10px; +} +``` + +This can be useful to define direction specific animations: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + ltr: { + animationName: { + '0%': { left: '0% /* @noflip */' }, + '100%': { left: '100% /* @noflip */' }, + }, + }, + rtl: { + animationName: { + '100%': { right: '-100% /* @noflip */' }, + '0%': { right: '100% /* @noflip */' }, + }, + }, +}); +``` + +In this case automatic flipping is disabled and will produce less CSS: 2 classes instead of 4. + ## Nested selectors Use nested selectors responsibly in styles because they can cause "CSS rule explosion". @@ -787,115 +899,6 @@ function Checkbox(props) { } ``` -## RTL styles - -Griffel performs automatic flipping of properties and values in Right-To-Left (RTL) text direction. - -```js -import { makeStyles } from '@griffel/react'; - -const useClasses = makeStyles({ - root: { - paddingLeft: '10px', - }, -}); -``` - -⬇️⬇️⬇️ - -```css -/* Will be applied in LTR */ -.frdkuqy { - padding-left: 10px; -} -/* Will be applied in RTL */ -.f81rol6 { - padding-right: 10px; -} -``` - -Values than contain CSS variables (or our `tokens`) might not be always converted, for example: - -```js -import { makeStyles } from '@griffel/react'; - -const useClasses = makeStyles({ - root: { - // ⚠️ "boxShadow" will not be flipped in this example - boxShadow: 'var(--box-shadow)', - }, -}); -``` - -In this case, please apply your own styles with the text direction from the `useFluent()` hook: - -```js -import { makeStyles } from '@griffel/react'; - -const useClasses = makeStyles({ - root: { - boxShadow: 'var(--box-shadow)', - }, - rtl: { - boxShadow: 'var(--box-shadow-in-rtl)', - }, -}); - -function App() { - const classes = useClasses(); - const { dir } = useFluent(); - const className = mergeClasses(classes.root, dir === 'rtl' && classes.rtl); - - /* --- */ -} -``` - -### Using `@noflip` - -You can also control which rules you don't want to flip by adding a `/* @noflip */` CSS comment to your rule: - -```js -import { makeStyles } from '@griffel/react'; - -const useClasses = makeStyles({ - root: { - paddingLeft: '10px /* @noflip */', - }, -}); -``` - -⬇️⬇️⬇️ - -```css -/* Will be applied in LTR & RTL */ -.f6x5cb6 { - padding-left: 10px; -} -``` - -This can be useful to define direction specific animations: - -```js -import { makeStyles } from '@griffel/react'; - -const useClasses = makeStyles({ - ltr: { - animationName: { - '0%': { left: '0% /* @noflip */' }, - '100%': { left: '100% /* @noflip */' }, - }, - }, - rtl: { - animationName: { - '100%': { right: '-100% /* @noflip */' }, - '0%': { right: '100% /* @noflip */' }, - }, - }, -}); -``` - -In this case automatic flipping is disabled and will produce less CSS: 2 classes instead of 4. - [fluent-colors]: https://react.fluentui.dev/?path=/docs/theme-color--page [griffel]: https://griffel.js.org [griffel-aot]: https://griffel.js.org/react/ahead-of-time-compilation/introduction From 2e250750f140bf7d595512f3bcc4ab7a030bdbd0 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 18 Jan 2023 15:12:33 +0100 Subject: [PATCH 18/26] Apply suggestions from code review Co-authored-by: Amber --- rfcs/react-components/styles-handbook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 179cce37d7e02..49e7d42ffb603 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -341,7 +341,7 @@ const useClasses = makeStyles({ ### CSS variables caveat -Values than contain CSS variables (or our `tokens`) might not be always converted, for example: +Values that contain CSS variables (or our `tokens`) might not be always converted, for example: ```js import { makeStyles } from '@griffel/react'; @@ -515,7 +515,7 @@ const useClasses = makeStyles({ }); ``` -Exact colors should be never used in Fluent UI code and we do not recommend their use in applications either. The exception are [system-colors][mdn-system-colors] used by forced colors mode (inside media queries): +Exact colors should be never used in Fluent UI code and we do not recommend their use in applications either. The exception is [system-colors][mdn-system-colors] used by forced colors mode (inside media queries): ```js import { makeStyles } from '@griffel/react'; From f882ea2dabac09aaa5df79f68fe2507f4a4fedfb Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 18 Jan 2023 15:14:29 +0100 Subject: [PATCH 19/26] do not expand shorthands in makeResetStyles calls --- rfcs/react-components/styles-handbook.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 49e7d42ffb603..8916b247dc838 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -276,7 +276,7 @@ import { makeStyles, makeResetStyles, mergeClasses, shorthands } from '@griffel/ const useBaseClassname = makeResetStyles({ ':hover': { - ...shorthands.padding('4px'), + padding: '4px', /* other styles */ }, '::before': { @@ -284,7 +284,7 @@ const useBaseClassname = makeResetStyles({ content: "' '", }, '@media (forced-colors: active)': { - ...shorthands.padding('4px'), + padding: '4px', /* other styles */ }, }); From d6a77046565898930e0da08074da74199f359589 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 18 Jan 2023 15:19:39 +0100 Subject: [PATCH 20/26] make comment with a problem explicit --- rfcs/react-components/styles-handbook.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 8916b247dc838..6e5e14d9ac0e7 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -812,7 +812,7 @@ Keep selectors simple to produce reusable CSS rules: - Complicated selectors are hard to override as overrides should match component's styles ```js - // On component side + // On component's side (library code) makeResetStyles({ '> .some-classname': { '> .other-classname': { @@ -823,7 +823,8 @@ Keep selectors simple to produce reusable CSS rules: }, }, }); - // On consumer side 💥 + // 🟡 On consumer side (application code) + // Works, but it's hard for a consumer to guess it makeStyles({ foo: { '> .some-classname > .other-classname:hover': { From 693d7b2f5986666e06b4635be432f5ccca4bda85 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 18 Jan 2023 15:23:25 +0100 Subject: [PATCH 21/26] put a missing word --- rfcs/react-components/styles-handbook.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 6e5e14d9ac0e7..395f706b0549c 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -866,7 +866,7 @@ Instead of usage [input pseudo classes][mdn-input-pseudo-classes] in styles, pre - Produces less classes on an element - Selectors for overrides are simpler -- These pseudo classes are supported by input elements +- These pseudo classes are **only** supported by input elements ```jsx import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; From 66b803a3b449bd292ad3bb40d35ad7b9d797eabd Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 19 Jan 2023 14:51:24 +0100 Subject: [PATCH 22/26] Apply suggestions from code review Co-authored-by: Sean Monahan Co-authored-by: Makoto Morimoto --- rfcs/react-components/styles-handbook.md | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 395f706b0549c..b11f2544f8127 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -2,7 +2,7 @@ [@layeshifter](https://github.com/layershifter) -This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used in Fluent UI React v9) to style components. +This document covers how to use [Griffel][griffel] CSS-in-JS (used in Fluent UI React v9) to efficiently style components. ## Table of contents @@ -42,7 +42,7 @@ This document covers how to efficiently use [Griffel][griffel] CSS-in-JS (used i # Introduction -Griffel is a hybrid CSS-in-JS that features runtime option like any other CSS-in-JS and [Ahead-of-time compilation][griffel-aot] with [CSS extraction][griffel-css-extraction] to reduce runtime footprint and improve performance. +Griffel is a hybrid CSS-in-JS that features a runtime option like any other CSS-in-JS solution and [Ahead-of-time compilation][griffel-aot] with [CSS extraction][griffel-css-extraction] to reduce runtime footprint and improve performance. # Core concepts @@ -74,7 +74,7 @@ Griffel uses Atomic CSS to generate classes. In Atomic CSS every property-value ## `makeStyles` -`makeStyles` is used to define style permutations in components and is used for style overrides. Returns a [React hook][react-hook] that should be called inside a component: +`makeStyles` is used to define style permutations in components and is used for style overrides. It returns a [React hook][react-hook] that should be called inside a component: ```js import { makeStyles } from '@griffel/react'; @@ -102,7 +102,7 @@ const useClasses = makeStyles({ }); ``` -> 💡 **Note:** The most of the functions follow syntax in matching CSS properties, but each value should a separate argument: +> 💡 **Note:** Most of the functions follow syntax matching CSS properties, but each value should be a separate argument: ```js // ❌ Will produce wrong results: @@ -141,7 +141,7 @@ const useClasses = makeStyles({ // produces 6 classes (1 + 1 + 4 for expanded padding) ``` -> 💡**Tip:** Preview generated classes consider in the [Try out][griffel-try-out] sandbox. +> 💡**Tip:** Preview generated classes in the [Try out][griffel-try-out] sandbox. However, "CSS rule explosion" can happen when using nested selectors, [pseudo classes][mdn-pseudo-classes]/selectors and [At-rules][mdn-at-rules]: @@ -173,7 +173,7 @@ const useClasses = makeStyles({ // produces 26 classes ((4+4)+(4+4)+2+(4+4)) ``` -Such cases might be unavoidable by design, [the next section](#makeresetstyles) covers APIs to address this problem. +Such cases might be unavoidable by design, [the `makeResetStyles` section](#makeresetstyles) covers APIs to address this problem. ## `mergeClasses()` @@ -254,7 +254,7 @@ function App(props) { ## `makeResetStyles` -This API works similarly to `makeStyles` and is used to styles as a single monolithic class to avoid the "CSS rules explosion" problem. +This API works similarly to `makeStyles` and is used to generate styles as a single monolithic class to avoid the "CSS rules explosion" problem. ```js import { makeResetStyles } from '@griffel/react'; @@ -515,7 +515,7 @@ const useClasses = makeStyles({ }); ``` -Exact colors should be never used in Fluent UI code and we do not recommend their use in applications either. The exception is [system-colors][mdn-system-colors] used by forced colors mode (inside media queries): +Exact colors should never be used in Fluent UI code and we do not recommend their use in applications either. The exception is [system-colors][mdn-system-colors] used by forced colors mode (inside media queries): ```js import { makeStyles } from '@griffel/react'; @@ -533,7 +533,7 @@ const useClasses = makeStyles({ Styles written for components should follow these rules: -- base styles should contain the most of CSS definitions that are applicable to base state +- base styles should contain most of the CSS definitions that are applicable to base state - use `makeResetStyles` to define them - styles for permutations should be granular - base styles should not be duplicated by permutations @@ -635,7 +635,7 @@ function Component(props) { } ``` -Conditions to apply styles might be complex, in this case consider to extract them to separate variables: +Conditions to apply styles might be complex, in this case consider extracting them to separate variables: ```js // ✅ Do @@ -726,7 +726,7 @@ const useBaseClassName = makeResetStyles({ ### Apply classes directly to elements -Do not use nested selectors to target an element or slot if you can apply classes on directly on it. +Do not use nested selectors to target an element or slot if you can apply classes directly on it. ```jsx import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; @@ -755,9 +755,9 @@ function App(props) { const classes = useClasses(); return ( -
+
{/* 💡 Classes can be passed directly to this element */} -
+
); } From be8a770aae574c00de7b97dec4c831ea18cadf6c Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 19 Jan 2023 14:55:52 +0100 Subject: [PATCH 23/26] add an example for `shorthands` & `@noflip` --- rfcs/react-components/styles-handbook.md | 31 +++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index b11f2544f8127..0fa3292391cdf 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -673,7 +673,36 @@ const useClasses = makeStyles({ } ``` -This can be useful to define direction specific animations: +`@noflip` also works with `shorthands.*` functions: + +```js +import { makeStyles } from '@griffel/react'; + +const useClasses = makeStyles({ + root: { + ...shorthands.borderLeft('5px /* @noflip */', 'solid /* @noflip */', 'red /* @noflip */'), + }, +}); +``` + +⬇️⬇️⬇️ + +```css +/* Will be applied in LTR & RTL */ +.f1h8qh3y { + border-left-width: 5px; +} + +.f150p1cp { + border-left-style: solid; +} + +.f1sim4um { + border-left-color: red; +} +``` + +This feature can be useful to define direction specific animations: ```js import { makeStyles } from '@griffel/react'; From d9ba1baf2d4094740d585f15f52f117338890872 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 19 Jan 2023 15:00:23 +0100 Subject: [PATCH 24/26] improve base examples --- rfcs/react-components/styles-handbook.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rfcs/react-components/styles-handbook.md b/rfcs/react-components/styles-handbook.md index 0fa3292391cdf..3585f23167ad3 100644 --- a/rfcs/react-components/styles-handbook.md +++ b/rfcs/react-components/styles-handbook.md @@ -83,6 +83,12 @@ const useClasses = makeStyles({ button: { display: 'flex' }, icon: { paddingLeft: '5px' }, }); + +function Component(props) { + const classes = useClasses(); + + return