-
Notifications
You must be signed in to change notification settings - Fork 67
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
Add CSS Map #1492
Add CSS Map #1492
Conversation
Thank you for your submission! Like many open source projects, we ask that you sign our CLA (Contributor License Agreement) before we can accept your contribution. Already signed the CLA? To re-check, try refreshing the page. |
|
cb0f1c8
to
334d839
Compare
Co-authored-by: Jake Lane <[email protected]>
Co-authored-by: Jake Lane <[email protected]>
'&&', | ||
t.binaryExpression( | ||
'===', | ||
t.isStringLiteral(objectProperty) |
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.
I'm having a bit of trouble understanding this nested ternary expression - could it be made more readable somehow?
e.g. use of parentheses
A ? (B ? C : D) : E
(A ? B : C) ? D : E
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.
Or perhaps by extracting this ternary expression into a separate variable:
const someVariable = A ? B : (C ? D : E);
// ...
return ...
t.binaryExpression(
'===',
someVariable,
t.stringLiteral(property.key.name)
)
...
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.
A few thoughts
@@ -0,0 +1,12 @@ | |||
import { css, cssMap } from '@compiled/react'; | |||
|
|||
const styles = cssMap({ |
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: Let's simplify this to
import { css } from '@compiled/react'
const styles = css.create({
...
});
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.
Having cssMap
simplifies types. What's the reason that css.create
is preferred?
`); | ||
|
||
expect(actual).toInclude( | ||
'<div className={ax(["success"==="danger"&&"_syaz5scu _bfhk5scu","success"==="success"&&"_syazbf54 _bfhkbf54"])}/>' |
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.
Something weird is happening here. "success"==="danger"
can be statically evaluated to false
. Where does this comparison even come from though? I've got a feeling we're doing more work than needed with the css()
call here, risks having spooky action.
If I do this,
<div css={css(styles.a)} />
<div css={css(styles.b)} />
<div css={css(styles.c)} />
<div css={css(styles.d)} />
<div css={css(styles.e)} />
I shouldn't end up with each case considering all of a,b,c,d and e
. I was explicit in each
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.
Yea, you are right.
But I think the selected variant is runtime variable at most of cases. Statically access to a variant is "by-product". Users probably should use
const a = {
color: red
}
<div css={css(a)} />
instead of
const styles = {
a: {
color: red
}
}
<div css={css(styles.a)} />
Also, bundler drops the dead code ('apple' === 'orange') && '.aaaabbbb'
'<div className={ax(["success"==="danger"&&"_syaz5scu _bfhk5scu","success"==="success"&&"_syazbf54 _bfhkbf54"])}/>' | ||
); | ||
expect(actual).toInclude( | ||
'<div className={ax([\'danger\'==="danger"&&"_syaz5scu _bfhk5scu",\'danger\'==="success"&&"_syazbf54 _bfhkbf54"])}/>' |
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.
Quoting inconsistency here
* const Component = ({ borderStyle }) => <div css={css([ | ||
* borderStyle === 'none' && { borderStyle: 'none' }, | ||
* borderStyle === 'solid' && { borderStyle: 'solid'} | ||
* ])} /> |
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.
This looks like it's outputting an intermediate form. It's preferable for us to consolidate things into a 1-pass compilation rather than having multiple passes, because this is lossy and prevents us from understanding the original context (which is useful for optimisations like dropping the "success"==="danger"
case. What's inhibiting us from doing this in the same pass as the css
-prop -> className
transform?
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.
um...good point. Let me look into it more.
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.
consolidate things into a 1-pass compilation rather than having multiple passes
It's been an architectural thing.
- Compiled firstly evaluates user input, and compiles it to expression. e.g.
foo ? 'color: red': 'color: green'
- At later stage, then compiles it to classNames and sheets
I think we can change this two-pass compilation into 1-pass by using local transform, as mentioned from another comment. I will make sure that I will spike this 1-pass compilation before PR is merged.
}, | ||
}); | ||
|
||
export default ({ variant, children }) => <div css={css(styles[variant])}>{children}</div>; |
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.
Just walking my way through this...
- Just using javascript to choose the style
const styles = css.create({
danger: {
color: 'red'
},
success: {
color: 'green'
}
});
const map = {
danger: styles.danger,
success: styles.success
}
export default ({variant, children}) => <div css={css(map[variant])} >{children}</div>
Can compile to
const styles = {
danger: {
color: '<hash_color_red>'
},
success: {
color: '<hash_color_green>'
}
});
const map = {
danger: styles.danger, // == {color: <hash_color_red>}
success: styles.success // == {color: <hash_color_green>}
}
export default ({variant, children}) => <div css={css(map[variant])} >{children}</div>
- Consumer passes in a style itself
export default ({ extendedStyle, children }) => <div css={css(extendedStyle)}>{children}</div>;
can compile to
export default ({ extendedStyle, children }) => <div css={css(extendedStyle)}>{children}</div>;
You're combining those two cases together... to form
3.
const styles = css.match({
danger: {
color: 'red'
},
success: {
color: 'green'
}
});
export default ({ variant, children }) => <div css={css(styles[variant])}>{children}</div>;
seems like a nice shortcut to me, but I'm unsure if we need it to be a different and special construct vs what we could make possible with 1
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.
Compiled essentially transforms one CSS rule into three pieces.
- classNames
- sheets
- CSS variables (if there are variables cannot be resolved statically). For simplicity, let's ignore it.
We can conditionally apply classNames to DOM element, but sheets cannot. Hence a special construct help identifying variants (will be useful for lints too), and piece sheets together.
const styles = cssMap({
danger: {
color: 'red'
},
success: {
color: 'green'
}
});
export default ({ variant }) => <div css={css(styles[variant])} />;
gets compiled to
<CS>".a {color: red}", ".b {color: green}"</CS> // sheets need to include all possible CSS outcome
...
I think the approach you mentioned from 1 is more leaning on local tranforms (which is roadmapped 🙂). If we have local transforms:
const foo = css({
color: 'red'
})
const bar = cssMap({
danger: {
color: 'red'
},
success: {
color: 'green'
}
});
gets compiled to
const foo = {
className: ".a",
sheet: ".a{color:red}",
};
const bar = {
danger: {
className: ".a",
sheet: ".a{color:red}",
},
success: {
className: ".b",
sheet: ".b{color:green}",
}
}
We can easily use javascript to choose the style
<div css={css([foo, bar[variant]])} />
gets compiled to
<CS>{[foo, bar]}</CS>
<div className={ax(foo.className, bar[variant].className])} />
If I misunderstood the comment, let's have a quick zoom call to clarify it (maybe)?
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: moving css-map.test.ts
to packages/babel-plugin/src/utils/__tests__/
perhaps
(unless there's a reason to have it in the css-prop
directory instead)
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.
I think it makes sense to be in packages/babel-plugin/src/css-prop
, because they aren't unit tests against the functions of css-map.ts
. These tests rely on many things from css-prop
.
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.
Ah no worries 👍
meta: Metadata, | ||
evaluateExpression: EvaluateExpression | ||
): | ||
| { |
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: not sure what this extra pipe character does here
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.
I wonder why the eslint plugin doesn't catch this 🤔
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.
believe it or not. The extra pipe is enforced by prettier. 🤷♂️
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.
TIL
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.
LGTM, just got some nitpicks
An alternative implementation has been adopted. |
Add the support for
cssMap