-
Notifications
You must be signed in to change notification settings - Fork 216
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
Theming with first Alert theme implementation. #150
Conversation
Nice work, @kof. I've spent a lot of time thinking about the same thing, and done a lot of work recently with @jossmac and @josephg on proofs of concept around the same things. After trying to use JSS in a production project that needs server-side rendering, we've found two major blockers; non-deterministic generation of class names is the big one (this is solved by aphrodite and css-modules) and having no way to rehydrate the JSS state on client-side render is the other (also solved by aphrodite, and related to their solution for deterministic class names) Adding and removing stylesheets as they are used (which is done by The other two major issues we've found, not specific to server-side rendering, and both solvable (imo) are:
To give some more background, that project I mentioned - we wrote it initially with JSS (and loved it, by the way, huge props for the work you have done on it) but for the above reasons had to switch over to aphrodite late in development. In good news, it wasn't that much to change, the two are fairly easy to port between (except for all the If you're happy to work through the above issues with us, I would love to use Elemental and our other projects as real-world cases to establish Let me know what you think, and I'll ping @nikgraf and @mxstbr for their feedback too - Max is taking a lead role in Keystone's development at the moment which is one of the most significant uses of Elemental at the moment, and I know Nik has interest in this space too with belle and react-future-ui |
hi @JedWatson , great feedback, I wish you would ask those questions as they appear to you and collaborate on finding the proper solution once you got a real world use case. Some of this things are true, some are not. Lets focus in this pull request on just theming, as it is not related to JSS. |
Lets continue the jss discussion here |
@kof and I have been discussing something similar for react-virtualized and so I thought I'd raise a common concern: we should make it easier for users to make small/incremental customizations to styles. The
I think both usability concerns could be mitigated by exposing the object currently created by export function getDefaultAlertTheme () {
return {
classes: {
danger: 'Alert Alert--danger',
error: 'Alert Alert--danger',
info: 'Alert Alert--info',
success: 'Alert Alert--success',
warning: 'Alert Alert--warning'
}
}
}
export function createAlertTheme (customTheme, replace = false) {
// Obviously we could recurse the theme object; I'm just being lazy.
return {
classes: fillInKeys(getDefaultAlertTheme().classes, customTheme.classes, replace)
}
}
function fillInKeys (defaultTheme, customTheme, replace) {
const theme = {}
Object.keys(defaultTheme).forEach((key) => {
if (customTheme[key]) {
theme[key] = replace
? customTheme[key]
: `${defaultTheme[key]} ${customTheme[key]}`
} else {
theme[key] = defaultTheme[key]
}
})
return theme
} Edit: The above might look like overkill for such a small theme, and maybe it even is. But I think if larger components embrace this approach to theming- it's important to provide a lightweight way for users to make small customizations. |
In other words we need either to expose default theme to the user or we need to merge users theme with the default one, in order to change parts of it. I would say we should expose the default theme object to the user, because merging at runtime is an overhead. Any suggestions where to put the theme object? Ideally it should go together with styles. But in our case styles are in less dir, we can't put js file there ... I would rename less dir to theme. Then put theme js files near the component less files:
|
This would be a pretty small overhead to pay as long as it was only done once per component. I'd say in this case it would be more important to prioritize ease of use over performance. Unless the default theme is accessible programmatically, at runtime, users are going to have to fork it which will lead to maintenance headaches. |
My assumption is that a theme is not something user is likely to modify at any render time. I see a theme as something user defines once statically and uses everywhere. |
Yes, I think so too. Which is why I didn't share your concern about the overhead of providing a merge method. It seems like something that's done once and so the overhead is probably really small. |
Well we can avoid remerging over and over by comparing the theme references, however not sure if it is worth it. We could leave it in the user space and let the user merge and cache it. I see it more like an optimization step, when we really get complains: "theme merging is too complex, component should do it". |
I see 3 primary use-cases for component theming:
I think the first 2 use-cases are simple to handle regardless of what we've been discussing. I'm more concerned with the 3rd. As a user, I already know I wouldn't want to be responsible for looking at source code and copy-paste-forking a theme just to make a small change. I'd much rather have some automation involved. I don't mean to be overstepping my bounds. It's just that since we've been discussing this same approach for react-virtualized, I have a fairly strong opinion on what should be kept easy for a user vs what might cause complaints/maintenance headaches. Seems it would be pretty easy to address this concern by exposing a lightweight merge method (similar to what I sketched above) that users could use like so~ import React, { Component } from 'react'
import { Alert } from 'elemental'
// Custom theme just overrides the red used for the danger warning but leaves all other styles alone
const customTheme = Alert.createCustomTheme({
classes: {
danger: {
background-color: #aa0000;
}
}
}, false)
class MyComponent extends Component {
render () {
return (
<Alert theme={customTheme} type="danger">
I am an alert!
</Alert>
)
}
} |
Well, don't see what is hard in this: import Alert from 'elemental'
import theme from 'elemental/theme/Alert'
import merge from 'lodash/object/merge'
const customTheme = merge({}, theme, {
classes: {
danger: '.some-class'
}
}) User can also optionally create a Themed higher order component which will always apply the custom theme. |
const customTheme = Alert.createCustomTheme({
classes: {
danger: {
background-color: #aa0000;
}
}
}, false) @bvaughn in this example it seems like you put inline styles into classes hash, which is a map of names and class names by https://github.com/kof/theme-standard/blob/master/spec.md Not sure if you did it by accident ... |
Good point about Lodash merge. That's true then I guess, as long as the default theme object is exported. :) |
theme: { | ||
classes: { | ||
danger: 'Alert Alert--danger', | ||
error: 'Alert Alert--danger', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: surely you mean 'Alert Alert--error'
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both works, can change it to error, in general its a bad thing to have different names for the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MikeMcElroy if you checkout line 7 error
is an alias for danger
.
I would like to move forward on this and to do so I would:
|
I just learned this url https://github.com/pulls and opened this PR out of curiosity and wow ... it's 4 years ago 🤣 I need to meet @bvaughn in person, because I know him virtually longer than I know my wife 🤦♂️ |
Maybe we'll be able to meet once all of this COVID stuff is over! |
This is a follow up on #53
I was hesitating with jss integration because of a number of unsolved problems. In fact it's not elemental specific problems, it's those of the entire react ecosystem.
To allow all this, I have started to work on a theme-standard
As any standard, it needs to get a real life proof and learn from real projects.
This PR is an attempt to implement ideas from this standard and produce a proof of concept for it.
For the Elemental library this will be the first step in this direction, the next steps are: