From 2ca94c8f0525b2a96a52988278ce8050f1a9cbbd Mon Sep 17 00:00:00 2001 From: Erik Lumme Date: Wed, 13 Sep 2017 11:27:27 +0300 Subject: [PATCH] Migrate WidgetStylingUsingOnlyCSS --- .../WidgetStylingUsingOnlyCSS.asciidoc | 172 ++++++++++++++++++ documentation/articles/contents.asciidoc | 1 + 2 files changed, 173 insertions(+) create mode 100644 documentation/articles/WidgetStylingUsingOnlyCSS.asciidoc diff --git a/documentation/articles/WidgetStylingUsingOnlyCSS.asciidoc b/documentation/articles/WidgetStylingUsingOnlyCSS.asciidoc new file mode 100644 index 0000000000..609c8a566b --- /dev/null +++ b/documentation/articles/WidgetStylingUsingOnlyCSS.asciidoc @@ -0,0 +1,172 @@ +[[widget-styling-using-only-css]] +Widget styling using only CSS +----------------------------- + +The preferred way of styling your widget is to only use static CSS +included in the theme's styles.css file. For information on how to +create custom themes, please refer to +https://vaadin.com/book/-/page/themes.creating.html. Your component can +be styled using the CSS class names that are defined to the widget using +`setStyleName`. + +Normal styling of components works in the same way as any styling using +CSS, but there are some special features to pay attention to when Vaadin +7 is used. + +[[some-sizing-theory]] +Some sizing theory +~~~~~~~~~~~~~~~~~~ + +All Vaadin 7 components get the CSS class v-widget which sets the +box-sizing to border-box. This causes borders and paddings to be +considered when the browser calculates the component's size. This means +that e.g. a component with padding: 5px and width: 100% inside a 200px +wide slot will fill the slot without any overflow because the inner +width of the component will be calculated to 190px by the browser. + +The Vaadin 7 server side API allows setting the size of the component in +three different ways: + +* Undefined size, set e.g. using `setWidth(null)`, means that the +component's size should have it's default size that might vary depending +on its content. +* Relative size, set e.g. using `setWidth("100%")` means that the +component's size is determined by the size and settings of the +component's parent. +* Fixed size, set e.g. using `setWidth("250px")` or `setWidth("10em")` means +that the component's size is fixed, so that the parent doesn't affect +the size and neither does the component's content. + +The three different ways of setting the size means that a component +should both support having its own natural size and filling the +allocated space depending on how the size is set. This usually means +that the main area of the component should adjust based on the settings. + +[[a-simple-sample]] +A simple sample +~~~~~~~~~~~~~~~ + +Consider e.g. a simple date picker component with a text field where a +date can be entered and where the currently selected is displayed and a +button that is used to open a calendar view where a date can be picked +using the mouse. + +[source,java] +.... +public class MyPickerWidget extends ComplexPanel { + public static final String CLASSNAME = "mypicker"; + + private final TextBox textBox = new TextBox(); + private final PushButton button = new PushButton("..."); + + public MyPickerWidget() { + setElement(Document.get().createDivElement()); + setStylePrimaryName(CLASSNAME); + + textBox.setStylePrimaryName(CLASSNAME + "-field"); + button.setStylePrimaryName(CLASSNAME + "-button"); + + add(textBox, getElement()); + add(button, getElement()); + + button.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + Window.alert("Calendar picker not yet supported!"); + } + }); + } +} +.... + +We then add this basic styling to the theme CSS file + +[source,scss] +.... +.mypicker { + white-space: nowrap; +} +.mypicker-button { + display: inline-block; + border: 1px solid black; + padding: 3px; + width: 15px; + text-align: center; +} +.... + +`display: inline-block` makes the button continue on the same line as the +text field, placing it to the right of the field. We also add +`white-space: nowrap` to the main div element to ensure the button is not +wrapped to the next row. Finally, there is some padding and a border to +make the button look more like a real button. + +[[using-available-space]] +Using available space +^^^^^^^^^^^^^^^^^^^^^ + +This simple layout works well as long as the component's has it's +default undefined width. Changing the width from the server does however +not have any visible effect because only the size of the main div is +changed. If the component is made smaller, the contents goes beyond the +size of the element (this can be verified by adding `overflow: hidden;` to +`.mypicker`) and if it gets larger the extra space is just left as empty +space to the right of the button. + +The first step towards making the size adjust is to make the text field +adjust to the main div element's width whenever the width is something +else then than undefined. In these situations, Vaadin 7 adds a +`v-has-width` class to the component's main element (`v-has-height` added +when the height is not undefined). + +[source,scss] +.... +.mypicker.v-has-width > .mypicker-field { + width: 100%; +} +.... + +With this additional CSS, the text field directly inside a picker that +has a defined width gets full width. + +[[making-room-for-the-button-again]] +Making room for the button again +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We're however not done yet. Setting the width of the text field to 100% +makes it as wide as the main div is, which means that the button goes +outside the main div. This can be verified using the DOM inspector in +your browser or by setting `overflow: hidden` to the style for `mypicker`. +To reserve space for the button, we can add some padding to the right +edge of the main div element. This does however cause the padding space +to remain unused if the component size is undefined. To compensate for +this, negative margin can be added to the right edge of the button, +effectively reducing its occupied size to 0px. + +Finally, we need to use `box-sizing: border-box` to make the field's +borders and paddings be included in the 100% width. + +The full CSS for the sample component: + +[source,scss] +.... +.mypicker { + white-space: nowrap; + padding-right: 23px; +} +.mypicker-button { + display: inline-block; + border: 1px solid black; + padding: 3px; + width: 15px; + text-align: center; + margin-right: -23px; +} +.mypicker.v-has-width > .mypicker-field { + width: 100%; +} +.mypicker-field { + -moz-box-sizing: border-box; + -webkit-boz-sizing: border-box; + box-sizing: border-box; +} +.... diff --git a/documentation/articles/contents.asciidoc b/documentation/articles/contents.asciidoc index 805407fcf7..5c4d262110 100644 --- a/documentation/articles/contents.asciidoc +++ b/documentation/articles/contents.asciidoc @@ -71,5 +71,6 @@ are great, too. - link:PackagingSCSSOrCSSinAnAddon.asciidoc[Packaging SCSS or CSS in an add-on] - link:RightAlignComparableNumericalFields.asciidoc[Right-align comparable numerical fields] - link:CustomizingComponentThemeWithSass.asciidoc[Customizing component theme with Sass] +- link:WidgetStylingUsingOnlyCSS.asciidoc[Widget styling using only CSS] - link:CreatingAUIExtension.asciidoc[Creating a UI extension] - link:CreatingAThemeUsingSass.asciidoc[Creating a theme using Sass] -- 2.39.5