-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
[styles] POC around the stable v4 API #14999
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
Conversation
8d66aaa to
317adbb
Compare
317adbb to
598c6a3
Compare
Not so sure about that one. I'd rather have css source maps. I fear people will abuse this to get deterministic class names. I guess this won't be merged as-is but rather split up into multiple PRs? Otherwise this is to much to review. Especially
I currently don't understand how this wouldn't be a breaking change and I'm still missing a minimal example for both APIs. Would like to discuss this separately.
I don't see the benefit here? How do you make this safe? Simply renaming doesn't magically solve any issues with CSS that CSS-in-JS solutions tried to solve. I feel like we're moving back to global CSS without having a discussion about it. |
@eps1lon This pull request will be split into multiple chunks. I'm interested in making the whole story fit together & to collect early feedback with some concrete code.
CSS 3 supports substring-matching selectors. So yes, people could do: div[class^="MuiButton-root-"]What would be the problem with people abusing it to get deterministic class names? Given the API we already expose, IMHO, all our CSS is already public, at the exception of few components that we label private and that we can keep nondeterministic. I was thinking of making Yes, CSS source map would help: cssinjs/jss#469.
It's not a breaking change as it won't break people code. All our existing capabilities will continue to work. We can later introduce a deprecation to reduce the number of ways people can implement the same behavior. At the consumption, if we move into this direction, the diff will be: --- a/docs/src/pages/demos/tabs/CustomizedTabs.js
+++ b/docs/src/pages/demos/tabs/CustomizedTabs.js
@@ -59,7 +59,7 @@ const useStyles = makeStyles(theme => ({
color: '#40a9ff',
opacity: 1,
},
- '&$tabSelected': {
+ '&.selected': {
color: '#1890ff',
fontWeight: theme.typography.fontWeightMedium,
},
@@ -67,7 +67,6 @@ const useStyles = makeStyles(theme => ({
color: '#40a9ff',
},
},
- tabSelected: {},
typography: {
padding: theme.spacing(3),
},
@@ -89,21 +88,21 @@ function CustomizedTabs() {
<Tabs
value={value}
onChange={handleChange}
- classes={{ root: classes.tabsRoot, indicator: classes.tabsIndicator }}
+ className={classes.tabsRoot}
>
<Tab
disableRipple
- classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
+ className={classes.tabRoot}
label="Tab 1"
/>
<Tab
disableRipple
- classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
+ className={classes.tabRoot}
label="Tab 2"
/>
<Tab
disableRipple
- classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
+ className={classes.tabRoot}
label="Tab 3"
/>
</Tabs>Being able to use the className API over the classes API is an important win for styled components users. Also, reducing the number of required object to provide to style a component is a win for plain CSS users. What do you have in mind as a minimal example for both API? I believe the diff illustrate how it changes between the current API and the proposed one. Now, I'm wondering if we need to change the core components. This behavior can be fully implemented at the
Exactly, it's the whole point, to "go back" to global CSS. This preliminary pull request tries to seed this discussion. Now, not everything will be global, the class names generated when doing css = f(props) can't be public, the components without the name attribute won't be public either.
It hasn't for solving the global CSS problem, sure we went with the default JSS behavior, we were scared of committing to a public CSS API. But here we are, after the introducing of the classes API, we can argue that our styles API is already public. cc @siriwatknp would it help? There is another correlated problem I'm not sure how to solve. class names are case insensitive, the convention is to use kebab-case, and yet, we are using PascalCase. |
Because we now add an additional constrain on our default It's fragmenting our API again. We have a styling solution. Why offer two?
That should be properly discussed in an RFC not just served as-is. |
|
This comment has been minimized.
This comment has been minimized.
@eps1lon Thanks, I have fixed the diff.
Yes, It's a draft, it's meant to go to the trash and to iterate on it.
We can take this example: https://codesandbox.io/s/zxmxjlx34.
import React from "react";
import { render } from "react-dom";
import { create } from "jss";
-import JssProvider from "react-jss/lib/JssProvider";
-import { createGenerateClassName, jssPreset } from "@material-ui/core/styles";
+import { StylesProvider, jssPreset } from '@material-ui/styles';
import StyledComponentsButton from "./StyledComponentsButton";
const styleNode = document.createComment("insertion-point-jss");
document.head.insertBefore(styleNode, document.head.firstChild);
-const generateClassName = createGenerateClassName();
const jss = create({
...jssPreset(),
insertionPoint: "insertion-point-jss"
});
const App = () => (
- <JssProvider jss={jss} generateClassName={generateClassName}>
+ <StylesProvider jss={jss}>
<StyledComponentsButton />
- </JssProvider>
+ </StylesProvider>
);
render(<App />, document.querySelector("#root"));
import React from "react";
import { render } from "react-dom";
-import { create } from "jss";
-import { StylesProvider, jssPreset } from '@material-ui/styles';
+import { StylesProvider } from '@material-ui/styles';
import StyledComponentsButton from "./StyledComponentsButton";
-const styleNode = document.createComment("insertion-point-jss");
-document.head.insertBefore(styleNode, document.head.firstChild);
-
-const jss = create({
- ...jssPreset(),
- insertionPoint: "insertion-point-jss"
-});
-
const App = () => (
- <StylesProvider jss={jss}>
+ <StylesProvider injectFirst>
<StyledComponentsButton />
</StylesProvider>
);
& .outline {
border-color: red;
}
- &:hover:not(.focused):not(.e):not(.e) .outline {
+ &:hover .outline {
border-color: yellow;
}
&.focused .outline {
-const StyledTextField = styled(({ className, ...props }) => (
- <TextField
- {...props}
- InputProps={{
- classes: {
- root: className,
- notchedOutline: "outline",
- focused: "focused"
- }
- }}
- />
-))`
- & .outline {
- border-color: red;
- }
- &:hover .outline {
- border-color: yellow;
- }
- &.focused .outline {
- border-color: green;
+const StyledTextField = styled(TextField)`
+ .mui-outlined-input {
+ & .mui-outlined-input-notched-outline {
+ border-color: red;
+ }
+ &:hover .mui-outlined-input-notched-outline {
+ border-color: yellow;
+ }
+ &.focused .mui-outlined-input-notched-outline {
+ border-color: green;
+ }
}
`; |
That's a different story now. You were talking about global CSS which affects every component. Something CSS-in-JS wants to avoid since of all the reasons that I don't want to reiterate. I'll ignore this for now until it's ready for review. I'm still a bit skeptical about certain implementations but we'll see when it's ready |
I have updated my previous comment with the history of why JSS:
@eps1lon Ok, I'm gonna start splitting this pull requests in different chunks. I'm gonna drop the
|
|
Allowing styling with plain class names makes class names a public api. It has bad and good parts in it.
|
@eps1lon I don't think that people doing CSS-in-JS care about the global or not nature of their CSS explicitly, they like the side effect. I believe most of the value comes from:
I don't think that Global CSS will change these two value propositions.
You are right, it's my biggest concern. I do think that both API have their own use cases:
@kof We already have this constraint, no?
overrides is an extension of configuration base theming when it's not enough. Configuration based theming should always be preferred. |
Currently it the property names in the theme. It is a bit more controlled since one can't nest things in unpredictable ways. But yeah, it's already there and it's not ideal since values are not strictly controlled for example shorthand properties.
I think once you open the chance of overriding using global classes, most will do that. Mb if it is at least not on by default and needs to be opted in and along with the flag we say this is not recommended. |
|
react-select is an interesting study case. It's a project that moved from global CSS to emotion. (Material-UI did global LESS -> inline styles -> JSS).
The react-autowhatever's approach he is mentioning is what we are already doing with the This comment is interesting, how is styled-components making overrides better? Could it be the other way around? No explanation of why and how.
People prefer to start from a reasonable simple styled state than from scratch.
People are targeting If we consider the number of upvotes in the issues (42 vs 79), moving to CSS-in-JS could have made things worse 🤔? |
@kof There are 3 layers. Right now, it's a dangerous option, it has been like this for 2 years. It has always worked with no overhead. Should we make it safe? Should we make it the default? I'm leaning toward making it the default. It's the strategy used by Bootstrap, Ant Design, Vuetify, eleme.io, 4 massively used libraries. It's not all bad. |
This is easy to conclude. In the end we know nothing about those users and their use cases. It is very different depending on skills level, time constraints, maintainability goals. My assumption is that our industry in its majority is populated with short-term projects and beginner level devs. That's why the fastest to learn API will find more users, even if it is worse. |
I didn't know we have that option. Do people fail to find it? Mb it's the placement in documentation? |
I think |
I personally had a lot of difficulties with this one - so anyway of simplifying specificity on some commonly override classes would be an improvement.
This one would be a major improvement in terms of being able to understand where the injection point happens (something both my team and I had difficulty understanding the semantics of); however, I think the larger issue that you briefly mentioned is that there should be a CLEAR encouraged approach - whether it is For a lot of beginners - since I had to onboard a quite a few, their natural inclination was to try to use CSS in JS to override styles of a component rather than |
|
I want to clarify this point. I think the biggest problem of JSS right now is that we cannot override nested styled children in parent? This sandbox can explain the issue. If we create new custom component using Does anyone have an idea to solve this problem ? without using global CSS would be great because I prefer writing sth like this than cc @kof |
|
I'm closing for #15140. |
@siriwatknp Here's a modified version of your sandbox with one approach: https://codesandbox.io/s/5zo9mm3y6x This assumes the child accepts a className prop and puts it on its root DOM element, but you could instead target the child DOM element type (e.g. "li") rather than using a class name.
|
This contains all the changes (some a breaking) I want to explore for the stable styles API of v4.
generateClassName->generateId. For reference: https://cssinjs.org/jss-api?v=v10.0.0-alpha.14.dangerouslyUseGlobalCSS->globalClassNames. The limitations haven't changed since we first introduced this option. It's pretty much like using theclassesAPI. I believe we can make it safe.ServerStyleSheets. It's an attempt to simplify the API: Simplify NextJs Example? Should we get the page context from renderPage? #14677MuiButton-root->mui-button-root).Propose to make any selector with a specificity > 1 to use a global class name. For instance (no breaking change):(bad idea)marginLeft: -1, borderLeft: '1px solid transparent', }, - '&$selected': { + '&.selected': { color: theme.palette.action.active, backgroundColor: fade(theme.palette.action.active, 0.12), '&:hover': { @@ -32,7 +32,7 @@ export const styles = theme => ({ marginLeft: 0, }, }, - '&$disabled': { + '&.disabled': { color: fade(theme.palette.action.disabled, 0.12), }, '&:hover': { @@ -42,7 +42,7 @@ export const styles = theme => ({ '@media (hover: none)': { backgroundColor: 'transparent', }, - '&$disabled': { + '&.disabled': { backgroundColor: 'transparent', }, }, @@ -102,8 +102,8 @@ class ToggleButton extends React.Component { className={clsx( classes.root, { - [classes.disabled]: disabled, - [classes.selected]: selected, + [`disabled ${classes.disabled}`]: disabled, + [`selected ${classes.selected}`]: selected, }, className, )}injectFirstproperty so the CSS injection change can be trivial.