- Notifications
You must be signed in to change notification settings - Fork13.4k
Button States
Any component that renders a button should have the following states:activated,disabled,focused,hover. It should also have aRipple Effect component added for Material Design.
A component that renders a native button should use the following structure:
<Host><buttonclass="button-native"><spanclass="button-inner"><slot></slot></span></button></Host>
Any other attributes and classes that are included are irrelevant to the button states, but it's important that this structure is followed and the classes above exist. In some cases they may be named something else that makes more sense, such as in item.
A mixin calledbutton-state() has been added to make it easier to setup the states in each component.
@mixinbutton-state() {@includeposition(0,0,0,0);position:absolute;content:"";opacity:0;}
The following styles should be set for the CSS to work properly. Note that thebutton-state() mixin is included in the::after pseudo element of the native button.
.button-native {/** * All other CSS in this selector is irrelevant to button states * but the following are required styles*/position:relative;overflow:hidden;}.button-native::after {@includebutton-state();}.button-inner {/** * All other CSS in this selector is irrelevant to button states * but the following are required styles*/position:relative;z-index:1;}
The activated state should be enabled for elements with actions on "press". It usually changes the opacity or background of an element.
Make sure the component has the correctcomponent structure before continuing.
Theion-activatable class needs to be set on an element that can be activated:
render(){return(<Hostclass='ion-activatable'><slot></slot></Host>);}
Once that is done, the element will get theion-activated class added on press.
In addition to setting that class,ion-activatable-instant can be set in order to have an instant press with no delay:
<Hostclass='ion-activatable ion-activatable-instant'>
/** * @prop --color-activated: Color of the button when pressed * @prop --background-activated: Background of the button when pressed * @prop --background-activated-opacity: Opacity of the background when pressed */
Style theion-activated class based on the spec for that element:
:host(.ion-activated).button-native {color:var(--color-activated);&::after {background:var(--background-activated);opacity:var(--background-activated-opacity); }}
Order is important! Activated should be before the focused & hover states.
Setting the activated state on the::after pseudo-element allows the user to customize the activated state without knowing what the default opacity is set at. A user can customize in the following ways to have a solid red background on press, or they can leave out--background-activated-opacity and the button will use the default activated opacity to match the spec.
ion-button {--background-activated: red;--background-activated-opacity:1;}
The disabled state should be set via prop on all components that render a native button. Setting a disabled state will change the opacity or color of the button and remove click events from firing.
Thedisabled property should be set on the component:
/** * If `true`, the user cannot interact with the button. */@Prop({reflectToAttr:true})disabled=false;
Then, the render function should add thearia-disabled role to the host, a class that is the element tag name followed bydisabled, and pass thedisabled attribute to the native button:
render(){const{ disabled}=this;return(<Hostaria-disabled={disabled ?'true' :null}class={{'button-disabled':disabled}}><buttondisabled={disabled}><slot></slot></button></Host>);}
Note: if the class being added was for
ion-back-buttonit would beback-button-disabled.
The following CSSat the bare minimum should be added for the disabled class, but it should be styled to match the spec:
:host(.button-disabled) {cursor: default;opacity:.5;pointer-events: none;}
TODO
The focused state should be enabled for elements with actions when tabbed to via the keyboard. This will only work inside of anion-app. It usually changes the opacity or background of an element.
Make sure the component has the correctcomponent structure before continuing.
Theion-focusable class needs to be set on an element that can be focused:
render(){return(<Hostclass='ion-focusable'><slot></slot></Host>);}
Once that is done, the element will get theion-focused class added when the element is tabbed to.
Components should be written to include the following focused variables for styling:
/** * @prop --color-focused: Color of the button when tabbed to with the keyboard * @prop --background-focused: Background of the button when tabbed to with the keyboard * @prop --background-focused-opacity: Opacity of the background when tabbed to with the keyboard */
Style theion-focused class based on the spec for that element:
:host(.ion-focused).button-native {color:var(--color-focused);&::after {background:var(--background-focused);opacity:var(--background-focused-opacity); }}
Order is important! Focused should be after the activated and before the hover state.
Setting the focused state on the::after pseudo-element allows the user to customize the focused state without knowing what the default opacity is set at. A user can customize in the following ways to have a solid red background on focus, or they can leave out--background-focused-opacity and the button will use the default focus opacity to match the spec.
ion-button {--background-focused: red;--background-focused-opacity:1;}
Thehover state happens when a user moves their cursor on top of an element without pressing on it. It should not happen on mobile, only on desktop devices that support hover.
Make sure the component has the correctcomponent structure before continuing.
Components should be written to include the following hover variables for styling:
/** * @prop --color-hover: Color of the button on hover * @prop --background-hover: Background of the button on hover * @prop --background-hover-opacity: Opacity of the background on hover */
Style the:hover based on the spec for that element:
@media (any-hover: hover) {:host(:hover).button-native {color:var(--color-hover);&::after {background:var(--background-hover);opacity:var(--background-hover-opacity); } }}
Order is important! Hover should be after the activated and focused states.
Setting the hover state on the::after pseudo-element allows the user to customize the hover state without knowing what the default opacity is set at. A user can customize in the following ways to have a solid red background on hover, or they can leave out--background-hover-opacity and the button will use the default hover opacity to match the spec.
ion-button {--background-hover: red;--background-hover-opacity:1;}
The ripple effect should be added to elements for Material Design. Itrequires theion-activatable class to be set on the parent element to work, and relative positioning on the parent.
render(){constmode=getIonMode(this);return(<Hostclass={{'ion-activatable':true,}}><button><slot></slot>{mode==='md'&&<ion-ripple-effect></ion-ripple-effect>}</button></Host>);
The ripple effect can also accept a differenttype. By default it is"bounded" which will expand the ripple effect from the click position outwards. To add a ripple effect that always starts in the center of the element and expands in a circle, set the type to"unbounded". An unbounded ripple will exceed the container, so addoverflow: hidden to the parent to prevent this.
Make sure to style the ripple effect for that component to accept a color:
ion-ripple-effect {color:var(--ripple-color);}