Button with Icon

I have a Button with an Icon

Button logoutButton = new Button("Logout", VaadinIcon.SIGN_OUT.create());

How can i define, where the icon should be?

Per default it is left of the text.

But i want it on top of the text.

I may have missed it, but last time I checked (this was in June) I couldn’t find a way to get the icon on the top.

I ended up writing a simple icon button with a polymer template:

<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/vaadin-icons/vaadin-icons.html">

<dom-module id="icon-button">
    <template>
        <style>
      :host {
		width: 80px; 
		height: 80px;
        display: flex;
        justify-content: center;
        align-items: center;
        color: var(--lumo-contrast-60pct);
      }

	  :host(:hover:not([disabled]
)), :host([focus-ring]
:not([disabled]
)) {
        color: var(--lumo-body-text-color);
	  }
	  
	  :host([disabled]
) {
	  	color: var(--lumo-contrast-30pct);
		pointer-events: none;
	  }
		
	  label {
        display: block;
        cursor: pointer;
        margin-top: .5em;
	  }

      iron-icon {
      	display: block;
      	margin: 0 auto;
        cursor: pointer;
        font-size: 2em;
      }
    </style>
    
<div class="box box2">
 <iron-icon id="iconx" icon="vaadin:plus"></iron-icon>
 <label id="label">Label</label>
</div>
</template>
    <script>
        class IconButton extends Polymer.Element {
            static get is() {
                return 'icon-button';
            }
            static get properties() {
                return {
                    // Declare your properties here.
                };
            }
        }
        customElements.define(IconButton.is, IconButton);
    </script>
</dom-module>

And

package bc.ui.components;

import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.PropertyDescriptor;
import com.vaadin.flow.component.PropertyDescriptors;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.HtmlImport;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.templatemodel.TemplateModel;

/**
 * A Designer generated component for the icon-button.html template.
 *
 * Designer will add and remove fields with @Id mappings but does not overwrite
 * or otherwise change this file.
 */
@Tag("icon-button")
@HtmlImport("src/components/icon-button.html")
public class IconButton extends PolymerTemplate<IconButton.IconButtonModel> {

   @Id("iconx")
   private Icon icon;
   @Id("label")
   private Label label;

   private boolean disabled;

   private static PropertyDescriptor<String, String> VALUE = PropertyDescriptors.propertyWithDefault("value", "");

   public String getValue() {
      return get(VALUE);
   }

   public void setValue(String value) {
      set(VALUE, value);
   }

   /**
    * Creates a new IconButton.
    */
   public IconButton() {
      // You can initialise any data required for the connected UI components
      // here.
   }

   public IconButton(String label, VaadinIcon icon) {
      setLabel(label);
      setIcon(icon);
   }

   public IconButton(String label, VaadinIcon icon, boolean enabled) {
      setLabel(label);
      setIcon(icon);
      setDisabled(!enabled);
   }

   public IconButton(String label, VaadinIcon icon, boolean enabled, ComponentEventListener listener) {
      setLabel(label);
      setIcon(icon);
      setDisabled(!enabled);
      addClickListener(listener);
   }

   public IconButton(String label, String icon, boolean enabled, ComponentEventListener listener) {
      this(label, VaadinIcon.valueOf(icon), enabled, listener);
   }

   /**
    * This model binds properties between IconButton and icon-button.html
    */
   public interface IconButtonModel extends TemplateModel {
      // Add setters and getters for template properties here.
   }

   public void setIcon(VaadinIcon icon) {
      this.icon.getElement().setAttribute("icon", "vaadin:" + icon.name().toLowerCase().replace('_', '-'));
   }

   public void setLabel(String text) {
      this.label.setText(text);
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   public Registration addClickListener(ComponentEventListener listener) {
      return this.addListener(ClickEvent.class, listener);
   }

   public boolean isDisabled() {
      return disabled;
   }

   public void setDisabled(boolean disabled) {
      if (this.disabled == disabled)
         return;

      this.getElement().setAttribute("disabled", "true");
      this.disabled = disabled;

   }
}

Hi hans-georg!

This should do it:

Button rightButton = new Button("Right",
        new Icon(VaadinIcon.ARROW_RIGHT));
rightButton.setIconAfterText(true);

Found the example from https://vaadin.com/components/button/java-examples

Hey Martin!

You should really avoid recreating existing components like this. Now your button is missing basic accessibility features like keyboard navigation and screen reader support.

I can suggest two better alternatives, reusing the existing Button component.

  1. Use the Element API:
Button button = new Button();
Icon icon = new Icon(VaadinIcon.ARROW_RIGHT);
button.getElement().appendChild(icon.getElement());
button.getElement().appendChild(new Div("Button label"));
  1. Use PolymerTemplate (like you did) and reuse vaadin-button:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/vaadin-button/vaadin-button.html">

<dom-module id="icon-button">
    <template>
		<vaadin-button>
			<iron-icon id="iconx" icon="vaadin:plus"></iron-icon>
			<div id="label">Label</div>
		</vaadin-button>
	</template>
    <script>
        class IconButton extends Polymer.Element {
            static get is() {
                return 'icon-button';
            }
            static get properties() {
                return {
                    // Declare your properties here.
                };
            }
        }
        customElements.define(IconButton.is, IconButton);
    </script>
</dom-module>

Thanks

now it works

Jouni Koivuviita:
Hey Martin!

You should really avoid recreating existing components like this. Now your button is missing basic accessibility features like keyboard navigation and screen reader support.

I can suggest two better alternatives, reusing the existing Button component.

  1. Use the Element API:
Button button = new Button();
Icon icon = new Icon(VaadinIcon.ARROW_RIGHT);
button.getElement().appendChild(icon.getElement());
button.getElement().appendChild(new Div("Button label"));
  1. Use PolymerTemplate (like you did) and reuse vaadin-button:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/vaadin-button/vaadin-button.html">

<dom-module id="icon-button">
    <template>
		<vaadin-button>
			<iron-icon id="iconx" icon="vaadin:plus"></iron-icon>
			<div id="label">Label</div>
		</vaadin-button>
	</template>
    <script>
        class IconButton extends Polymer.Element {
            static get is() {
                return 'icon-button';
            }
            static get properties() {
                return {
                    // Declare your properties here.
                };
            }
        }
        customElements.define(IconButton.is, IconButton);
    </script>
</dom-module>

Thank it work fine!

I am using the Material theme using Vaadin 18, and a Button with an Icon and no text and it is not centred - the padding appears either in front of, or after the icon depending on whether setIconAfterText is true or false.

The examples provided in the documentation show the icons correctly centred.

[https://vaadin.com/components/vaadin-button/java-examples]
(https://vaadin.com/components/vaadin-button/java-examples)

Any thoughts on this ?

On further testing, this appears to be an issue with the Materials Theme…