-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Description
Summary
We currently don't have a good story around how to style preact-based amp components.
Because of that, we've restricted ourselves to inline-styles for now, which are very limiting (e.g. no pseudo classes like :hover).
The design doc above goes though a number of ways forward, with a preference towards using a combination of jss and react-jss for the preact component, and compiling the jss into static css for amp/bento modes. My proposal here deviates from that plan slightly, by suggesting we should publish static css even for preact components.
Proposed solution
Write our code as if we are using jss and react-jss, but then compile to a css string for all modes.
This would mean:
- Write the
component.jss.jsfile as if we were using jss/react-jss. - During a
gulp build, we compile the jss.js file into bothcomponent.cssand acomponent.css.jswith two exports:useStylesandcssText.
2a.useStyles: An equivalent function to react-jss useStyles(), except with no side effects (does not add<style>tag to the DOM).
2b.cssText: The minified string of text for a css file with classnames that match up withclasses.
2c.component.css: This file has the same contents ascssText. - When writing
component.js, importusetStylesfrom the built file
How to write component styles with this proposal for amp/bento mode?
file: component.jss.js
export default {
component: {
display: 'flex',
position: 'relative',
overflow-x: 'auto',
},
button: {
fontStyle: 'italic',
'&:hover': {
outline: '2px solid blue''
},
},
}file: ../../../build/component/1.0/component.jss.js
const classes = {
component: 'component-1',
button: 'button-2'
}
export function useStyles() {
return classes;
}
export const cssText = `component-1 {...}, component-1:hover: {...}, button-2 {...}`file: preact-component.js
import {useStyles} from '../../../build/component/1.0/component.jss.js';
function Component(props) {
const classes = useStyles();
return (
<div className={classes.component}>
<button className={classes.button}>↣</button>
</div>
);
}file: amp-component.js
import {cssText} from '../../../build/component/1.0/component.jss.js';
class AmpComponent extends PreactBaseElement { ... }
AmpComponent.shadowCss = cssText;what about direct consumers of the preact component?
It is relatively common for UI libraries/components to expect consumers to include a css file. As examples, react-dates, blueprint, and bootstrap all expect this of consumers.
Notably, React Material UI, is an exception here. It bundles along jss and react-jss at runtime. While this seems to be the most ergonomic method for both publisher/consumer of the lib (reduced build step, easy theming/dynamic values), it has significant penalties for the end user (bundle size / performance).
file: my-app.js (assuming bundling with rollup, webpack, or parcel)
import '@ampproject/fit-text/styles.css';
import FitText from '@ampproject/fit-text';
...pros
- Best performance and therefore experience for end-users
cons
- No support for jss-style theming
- Requires consumers to import a css file
- Requires a build step
Alternatives
compiled css for amp/bento, direct usage of jss for preact consumers
pros
- AMP and Bento mode stay at maximal performance
- Preact consumers can provide custom themes.
- Preact consumers don't need to separately import a css file (ease of adoption).
cons
- Significant bundle size increase for even our smallest components (~30kb).
- Requires a build step
use jss/react-jss everywhere
pros
- Simplest to implement, understand
- Is the most flexible for consumers
cons
- Least performant option for everyone
/cc @ampproject/wg-approvers