Skip to content

I2I: Bento Styling #29862

@samouri

Description

@samouri

Summary

Design doc: https://docs.google.com/document/d/14gMbnzsTG3F1gxfbLck4kr7At62UYWa-DyrI3nDMJJw/edit?pli=1#heading=h.xn5fv5ul5g8p

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:

  1. Write the component.jss.js file as if we were using jss/react-jss.
  2. During a gulp build, we compile the jss.js file into both component.css and a component.css.js with two exports: useStyles and cssText.
    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 with classes.
    2c. component.css: This file has the same contents as cssText.
  3. When writing component.js, import usetStyles from 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

Metadata

Metadata

Assignees

Labels

INTENT TO IMPLEMENTProposes implementation of a significant new feature. https://bit.ly/amp-contribute-code

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions