Skip to content

Introduce a common API to style Button from both web and mobile#19104

Closed
dratwas wants to merge 33 commits intotrunkfrom
experimental/button/styled-system/emotion
Closed

Introduce a common API to style Button from both web and mobile#19104
dratwas wants to merge 33 commits intotrunkfrom
experimental/button/styled-system/emotion

Conversation

@dratwas
Copy link
Copy Markdown
Contributor

@dratwas dratwas commented Dec 12, 2019

Description

This is an exploration of x-platform style system. wordpress-mobile/gutenberg-mobile#1411
This is the second iteration on wordpress-mobile/gutenberg-mobile#1386

Mobile side PR: #19179

In this PR I used styled-system with emotion to create a primitive Button component that could be used in web and mobile and could be styled via props.

  • I added A and button components since both of them are used in @components/Button. They accept className as a prop to be backward compatibility ( we need to support already existing themes ) and all style props that are supported by styled-system
  • I added a PrimitiveButton that is described here - Introduce a common API to style Button from both web and mobile #19104 (comment)
  • I added the theme provider and theme values (they could be a bit outdated since I took them from my previous PR [Experimental] Cross platform Box component to replace div  #17614 )
  • I used created primitives in @components/Button (after that all buttons uses the primitives)
  • I passed styles via props instead of CSS in the Inserter button.
  • In addition, I created BlockBreadcrumbButton components that use PrimitiveButton instead of @components/Button just to demonstrate.

How has this been tested?

  • Run Gutenberg web
  • All buttons should look the same as before
  • Check if BlockBreadcrumbButton looks correct with and without hover/focus
  • Check if Inserter button looks correct
  • The preview button should be different than originally - it should be 50px height and has bigger borderRadius.

Screenshots

The BreadcrumbButton component that uses PrimitiveButton with additionalStyles prop to add hover, focused styles - https://github.com/WordPress/gutenberg/pull/19104/files#diff-21bd8baf92167b81915be200a8dd9eb8
Screenshot 2019-12-17 at 12 59 43

:hover

Screenshot 2019-12-17 at 12 59 33

:hover:focus

Screenshot 2020-02-10 at 15 57 35

:hover:focus:active

Screenshot 2020-02-10 at 15 57 50

The Inserter button that uses @components/Button with some changed styles by additionalStyles and styled-system props - https://github.com/WordPress/gutenberg/pull/19104/files#diff-c6139f159c9b2b79af303587407aea51

Screenshot 2019-12-17 at 13 01 47

:hover

Screenshot 2020-02-10 at 15 58 07

:hover:focus

Screenshot 2020-02-10 at 15 58 41

:hover:focus:active

Screenshot 2020-02-10 at 15 59 02

Types of changes

Experimenting with emotion and styled-system

PrimitiveButton checklist

  • States
  • States change
    • on the web, we can use onFocus, onBlur to determine the focus state. MouseEvents to determine the active and hover state. We specify whether the button is enabled or disabled by prop so we don't need to listen to that change. The only state left is visited but I'm not sure if we can get that information somehow and also if the visited is also applicable to button or only to the a element.
  • Icons & Labels
  • Classname
  • Visibility
  • CSS prop

components/Button checklist

  • Components/Button should have the same props PrimitiveButton has + variants
  • Styles passed via props will override variant styles
    • They do but i.e even if i use marginLeft in the inserter button, this is not applied because of styles that are declared in a different place and are more specific (i think it should work like that because of themes that can override styles) but they override styles that are declared in components/Button
  • keep the backward compatibility

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR. .

@ItsJonQ
Copy link
Copy Markdown

ItsJonQ commented Dec 12, 2019

@dratwas I know you just started this. Just wanted to say that I'm really excited to see this happening :D

@dratwas dratwas force-pushed the experimental/button/styled-system/emotion branch from f8b4bf3 to d9b7907 Compare December 16, 2019 17:15
@dratwas dratwas changed the title [Experimental] Add button primitive [Experimental][emotion] Add button primitive Dec 16, 2019
@dratwas
Copy link
Copy Markdown
Contributor Author

dratwas commented Jan 31, 2020

There are notes made by @pinarol during our conversation about that PR. We will add the PrimitiveButton component.

PrimitiveButton

States

We’ll have a defined set of states:

  • focus
  • active
  • enabled
  • hover
  • disabled
  • visited

Each should be documented for each platform, not every one of them has to be available for both mobile and web but they should be limited to what has defined.

We should:

  • Allow style customizations for each state
  • Allow combining the states

This can be similar to:

additionalStyles={[{ states: 'focus:hovered:visited', styles: {...} }]}

State changes

We’ll provide event handlers to state changes if possible. Needs investigation.

Icons & Labels

Icons or labels won’t be part of PrimitiveButton API, they will be passed as children:

<PrimitiveButton hoveredStyle bg="primary" heigh={50} >
  <Text>Some text </Text>
  <Icon />
</PrimitiveButton>

classname

We should have the classname prop to allow the style overrides by themes.

::after ::before
It looks like we may need to support these in the API for web because we don’t have a way for getting informed by state changes. But this needs more investigation.

Visibility

This component will be public as it has some use cases in some plugins: https://github.com/Automattic/jetpack/blob/master/_inc/client/components/button/style.scss

CSS prop

There won’t be any css prop since it is violating x-platform usage.

components/Button

  • We don’t necessarily need to use the PrimitiveButton in it we can use emotion’s button
  • Components/Button should have the same props PrimitiveButton has + variants
  • Styles passed via props will override variant styles
<Button link marginLeft={5} height={50} >
  <Text>Some text </Text>
</Button>

...will render a link button with 2 difference only: marginLeft={5} height={50}

  • We’ll keep the backward compatibility

@pinarol pinarol requested a review from gziolo February 26, 2020 10:47
@pinarol pinarol added Needs Decision Needs a decision to be actionable or relevant Needs Technical Feedback Needs testing from a developer perspective. Needs Testing Needs further testing to be confirmed. labels Feb 26, 2020

export default function BlockBreadcrumbButton( { children, ...props } ) {
const theme = useTheme();
const additionalStyles = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for things that don't translate to mobile?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't want to expose the css prop in public API, there is no way to set styles for i.e hover, focus states. Our first idea was to use props like hoverStyle, activeStyle ... but these styles can be mixed so we will end with hoverFocusActiveEnabledStyle and that would be hard to use. So we decided to add additionalStyles prop where we can set styles for different states.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So these work for mobile too?

Copy link
Copy Markdown
Contributor Author

@dratwas dratwas Feb 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The short answer is “yes”, but not all of them. For example “hover” is invalid for mobile as there’s no cursor. We should have done a better job documenting these states. I’ll add it to this PR.

border={ 'none' }
outline={ 'none' }
padding={ 'medium' }
margin={ 0 }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that we want these props for a primitive but I don't feel very good having these leak to a higher-level abstraction (UI components) I'd rather have variations in the component itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can really use your and other reviewers’ guidance at this point about how to make this better. We also had concerns about overwriting styles for variants like isPrimary, isSecondary, however, we found in many places that Button style is overridden in CSS in addition to using variants, like here or here. The idea behind this is, being able to meet the same need in an x-platform way. Otherwise, those kinds of customizations will not work on mobile. We could refactor these Buttons that have overridden styles to use PrimitiveButton instead but it will require a lot of extra work. I really like what @pinarol suggested to put all styling params like margin, padding (not sure about additionalStyles) etc. to one and call it experimentalStyles, in that way we will expose only one prop in public API, WDYT about it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

however, we found in many places that Button style is overridden in CSS in addition to using variants, like here or here.

For me instead of doing that, I prefer if we should be looking at why we did these overrides in the first place and avoid them entirely with better abstractions/components/variations. These overrides quickly lead to bugs anyway.

Copy link
Copy Markdown
Contributor Author

@dratwas dratwas Feb 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was something we were questioning as well, “should we allow overriding the styles already defined in variants”? The ideal answer to this is actually “no”, this is also how syled-system variants page recommends: https://styled-system.com/variants given that you are sharing a similar opinion I think we can remove the props that are already defined by variants. However, we’ll still need to provide some props to be able to position the Button in a x-platform way like here or here. Positioning/layout related props need to be available among all x-platform components not just Button, and those are: space, layout, flexbox, position from https://styled-system.com/api Otherwise we won’t be able to provide an x-platform component, wdyt?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain I understand exacly why we need the positionning props absolutely. There are alternatives, Layout components (flex containers aware of their children,toolbars...). I'm not saying we won't ever need these but I believe it's better to start with no CSS props at all and consider use-case per use-case if there are better alternatives.

Copy link
Copy Markdown
Contributor Author

@dratwas dratwas Feb 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are alternatives, Layout components (flex containers aware of their children,toolbars...).

Agree, but sometimes it's not enough and the 'margin', 'width' etc are needed. At this moment they are set in CSS like here but we are not able to do the same on mobile. So we would need to wrap the button into some container (View) and add the margins to the container which is not performant and adds one more nesting level.

I'm not saying we won't ever need these but I believe it's better to start with no CSS props at all and consider use-case per use-case if there are better alternatives.

That's actually a good idea to do less in that PR. We could get rid of these props and then add them if needed during refactoring. However, I still feel like adding the spacing props will be one of the first things that will happen. There is a small part of the list of components that overrides these styles:


min-height: $icon-button-size;





margin-right: $grid-unit-15;

margin-left: 10px; // equal to standard button inner padding

I also checked for example the rebassjs lib that is UI component library and they allow to set i.e margin to button that is primary https://rebassjs.org/button/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we referred to the already existing css customizations for Button component while adding the new style props. However I am now discovering that we actually don't quite approve most of those customizations either. What I am confused about is, isn't this a good reason to lean towards to a more encapsulated API design?

If we hide the css inside the component and manage styles via props we’ll have a possibility to deprecate/remove those which we don't want to be overridden.

Currently we are discussing what to “expose” in the API and I think it is a very valuable discussion to have. But doesn’t current state already expose every overriding possibility by providing the className publicly in the API? Also as far as I see we are already tied to a specific class name(components-button), just to provide overriding capabilities I assume?

If we look at it this way this PR actually isn’t extending what is already exposed. On the contrary I think it can be a first step to be in control of what to expose in the future.

Having said that, I think it’d be good to group style related props under one experimental prop at this stage, that would make us more free about trying different possibilities.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About having margins/width/height in the API: I am thinking what can be the alternative approach?

  1. Designing the Button in such a way that always fills the container?

This requires putting a container per every Button just to be able to set margins/width/height, thus, doesn’t feel right.

  1. Determining margins/width/height inside the implementation, so nobody would be able to override?

And this doesn’t look like a realistic goal considering almost every usage of Button sets a different margin. Plus, I believe it’s a fair need to change the margins/width/height of the component.

G,
HorizontalRule,
BlockQuotation,
PrimitiveButton,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left these exports here only for Backward compatibility, ideally we should just consume the primitives from the primitives package.

Let's not expose other primitives here.

export { default as withNotices } from './higher-order/with-notices';
export { default as withSpokenMessages } from './higher-order/with-spoken-messages';
export * from './text';
export { useTheme, default as ThemeProvider } from './theme-provider';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs more time to mature as it's the first CSS in JS public API, can we start with experimental APIs?


export default ( { theme, children } ) => (
<ThemeProvider theme={ getTheme( theme ) }>{ children }</ThemeProvider>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this introduces theming, I'd like to know more (and maybe even document) how do we build themable components, how are we planning to update our UI components to be themable and how to use these APIs in general.

Copy link
Copy Markdown
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work here.

Let's give this PR more visibility in the next #core-js #core-css chats and among the @WordPress/gutenberg-core team as it can be impactful.

@koke koke removed their request for review April 2, 2020 15:30
@gziolo gziolo added the Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) label Nov 28, 2020
@gziolo
Copy link
Copy Markdown
Member

gziolo commented Nov 28, 2020

What’s the status of this proposal? There is now many components redactored in @wordpress/componnents to use CSS in JS approach so it might be more relevant than ever. @hypest and @Tug, any thoughts?

@dratwas
Copy link
Copy Markdown
Contributor Author

dratwas commented Feb 10, 2021

Hey @gziolo, unfortunately, I do not know the status of this proposal since we were waiting for the decision from gutenberg/core team. I started doing other tasks and we never got answers to our questions ;(

I believe it is still valid even though it was a year ago. What I can suggest is to start from resolving conflicts then revisit all button styles (web and mobile) as I did previously and add new variants if any.

There is also an important discussion about props that can be passed to the components: #19104 (comment)

@hypest
Copy link
Copy Markdown
Contributor

hypest commented Feb 11, 2021

cc @chipsnyder

@gziolo
Copy link
Copy Markdown
Member

gziolo commented Feb 12, 2021

@dratwas, thank you for the update. It looks like this topic didn't get enough attention.

@ItsJonQ is leading efforts to create a new component system designed with the goal to become the primary way to build UIs. He is working on an official proposal to have it included in the WordPress core. At the moment, it's web-only, but it uses the architecture which should make it possible to integrate it with mobile in a similar way as here.

@ItsJonQ and @sarayourfriend should move the code that abstracts styles to the repository quite soon. All the code for the component system is located in the following folder packages/components/src/ui/. It's still in development, so there is no final public API defined. I'm sure @ItsJonQ has good opinions on how it should be shaped but if you would discover any blockers from the mobile side, let us know.

@ItsJonQ
Copy link
Copy Markdown

ItsJonQ commented Feb 16, 2021

Thanks for the mention @gziolo !

For added context, the Style system for the new component system can be found here:
https://github.com/ItsJonQ/g2/tree/main/packages/create-styles

As @gziolo mentioned, it will be moved over to @wordpress/gutenberg as part of our integration.

Under the hood, it's using Emotion (v10), just like our current solution.
However, there are a lot of crucial enhancements, which are currently web specific - notably how variables work.
That being said.. A lot of how styles are created/accessed are done through functions that we have control over (e.g. styled, css(), or get() (for variables)). To add React Native support, we can adjust the behaviour of these functions specific for the RN platform (in theory).

It's not something I've explored yet.

My personal RN experience has been limited to the Expo development experience.
For my app project, I used emotion for styling, so I have some RN x Emotion experience in that sense.

Base automatically changed from master to trunk March 1, 2021 15:42
@dratwas dratwas closed this Jul 10, 2023
@gziolo gziolo deleted the experimental/button/styled-system/emotion branch July 10, 2023 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) Needs Decision Needs a decision to be actionable or relevant Needs Technical Feedback Needs testing from a developer perspective. Needs Testing Needs further testing to be confirmed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants