Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Styling and Theming Polymer elements

Styling and Theming Polymer elements

Recording of the talk: https://www.youtube.com/watch?v=xSnG-mNWhqI (the beginning of the talk was unfortunately not recorded, but it only contained a bit of background).

A presentation I gave at Vaadin pre-summit meetup 2017 in Copenhagen, Aug 21st 2017. I talk about the problems around styling web components in general, and then describe the solution that we at Vaadin implemented to offer a flexible but still robust styling API for our Polymer Elements.

Jouni Koivuviita

August 21, 2017
Tweet

Other Decks in Technology

Transcript

  1. VAADIN PRE-SUMMIT MEETUP Aug 21st 2017 • Hi, and thanks

    for coming to meet us today • My name is Jouni, and I’m a product designer at Vaadin • I’ve been working with web apps at Vaadin for over 11 years already…
  2. THEMING AND STYLING WEB APPS …and the need to theme

    and style web apps is as big, or even bigger than before, as users expect and demand more polished experiences. During these years, the way we write CSS for our web apps has evolved many times…
  3. …with pre-processors like Sass, or systems like BEM and Atomic

    CSS creating new best practices. There’s been a clear, growing trend towards writing more modular, component-based CSS, as frontend development as a whole has been moving in that direction. I assume all of you are writing Polymer elements nowadays, so that is already very natural to you.
  4. Speaking of Polymer, and web components and shadow DOM more

    specifically, we have yet another change in front of us. The tightly scoped nature of shadow DOM requires us to rethink, and possibly unlearn, some of the ways we write CSS and provide styling APIs for our elements.
  5. Material design [Polymer] doesn't provide "lookless" elements – you're pretty

    much stuck with Material design. Better decoupling of look and feel from code (i.e. Paper essentially buys you into Material design). ? …I highlighted one of the pain points with Paper elements, how they don’t really allow you to change the style of the elements too much…
  6. Screenshots: materialpalette.com …mostly just affecting fonts and colors, but still

    looking an awful lot like Material design. And many times, we (or the designers in our teams) want something else. There are some individual web components and web component UI libraries available that offer a different type of look-n-feel, but many of them are just hobby projects, where you can’t rely on maintenance or support, or they don’t have a permissive license you can use. And most of them limit your options for styling to fonts and colors, just like Paper elements.
  7. Flexible customization API • • > Material design Maintenance and

    support • GOALS These are the exact problems we want to solve for you with the styling API of our elements and our themes. - Visually something more or other than Material design, to satisfy business goals (and personal preferences) - Flexible customization, with a clearly documented API, so we can be sure our styles will work even with future updates - Maintenance and support for the components, so you can expect them to keep working in the future and get help when needed
  8. S H A D O W D O M my-element

    /deep/ div @apply --mixin var(--custom-property) <div> Shadow DOM is a blessing and a curse for this, as it shields us from the global styles of the app, but at the same time limit our ways how to customize the element. As you know, you can’t target elements inside a shadow root with regular CSS selectors. Only inherited properties can pass through by default. In the first version of the spec, v0, there were special selectors that you could use to “pierce” the shadow boundary, and /deep/ was the most used one of those. But as you probably know by now, it was removed from the v1 spec, mostly because of performance reasons, and is no longer available for use. I can’t say I miss it, though, as it was a very “shotgun” approach to solving this problem, allowing any styles to be targeted inside the shadow DOM. Then there was CSS mixins, or @apply. A well known concept from pre-processors, but now with standards CSS. Sounds great, and something that could solve the styling API needs. We actually tried to build our styling API on top of mixins initially, but found out that they are really quite limited and cumbersome for that purpose. And we’re not alone, as there has been a lot of discussion about this with the standards committee, which eventually lead to discontinuing the work on CSS mixins and @apply as a standard – at least for now. If you want to know more about this, I can share a few links afterwards: - http://www.xanthir.com/b4o00 - https://discourse.wicg.io/t/needed-new-champion-for-css-apply-rule/2012/12 - https://github.com/w3c/webcomponents/issues/300 So, that leaves us with custom properties for our styling API. While those are an excellent addition, and provide a fair amount of styling flexibility, they suffer from the same problems as mixins, as mixins was basically just a concept on top of custom properties.
  9. S H A D O W D O M var(--custom-property)

    <div part="logical-piece"> my-element::part("logical-piece") my-element::theme("logical-piece") Luckily, there’s a new standards proposal for exposing parts of the shadow DOM for styling, named “Shadow Parts”, which the WebKit team has been promoting for quite a while already, and it seems to be the thing to bet on at the moment. Basically, it allows you to name certain elements inside your shadow DOM, which are then accessible with special CSS selectors from the outside. https://tabatkins.github.io/specs/css-shadow-parts/
  10. ROLE Designer Developer Manager <label part="label"> <div part="overlay"> <div part="menu">

    ... my-element::part(overlay) { box-shadow: ...; background-color: ...; } Designer Developer Manager This allows component authors to specify the styling API explicitly with the named parts, by promising to always have those logical or semantic parts in their components. Examples of parts could be a label for a field, or an overlay for a dropdown menu. The parts can be nested also, so inside that overlay, we could have a menu, and that menu has menu items, and so on. Then, we can use these new selectors to target those elements and apply our custom styling. Sounds great, right?
  11. The bad news is, it’s still very, very early for

    this spec, unfortunately. So, we have to come up with something else in the meantime :(
  12. THEMING AND STYLING POLYMER ELEMENTS That brings us to Polymer

    elements specifically, as our goal is to provide a flexible and robust styling API for our Polymer elements. Hopefully that encourages others to do the same and with that influence the standards to progress in a direction we need them. Heads up, this section is going to be quite code-heavy, so keep an eye on the slides.
  13. TARGET THE SHADOW PARTS PROPOSAL As a starting point, we

    chose to base our solution on the proposed standard, and define part attributes in our element shadow DOMs.
  14. <template> <div class="container"> <label part="label" ... >[[label]]</label> <div part="input-field"> <slot

    name="prefix"></slot> <input part="value" ... > <slot name="suffix"></slot> </div> <div part="error-message" ... >[[errorMessage]]</div> </div> </template> VAADIN-TEXT-FIELD.HTML Here’s an example how the template for vaadin-text-field looks like at the moment. These are the elements we expose as our styling API, and we promise not to remove these parts or change their hierarchy in maintenance or minor version updates. They are NOT very likely to change in major releases either, but if they do, we will document any breaking changes clearly. Notice, how the names aim to be semantic rather than represent the technical types of the elements, like with the native input element, which is named as “value”, as that part visually contains the textual value of the field. Visually, it’s the wrapping “input-field” element that defines the input’s borders and background.
  15. <style> [part="label"] { font-size: 0.875em; font-weight: 600; margin-bottom: 0.25em; }

    [part="input-field"] { border: 1px solid rgba(0, 0, 0, 0.3); background-color: #fff; padding: 0.25em; } :host([focused]) [part="input-field"] { VAADIN-TEXT-FIELD-DEFAULT-THEME Since the new CSS selectors for parts are not yet available, and we don’t want to write our own polyfill for them, as it would take a lot of time and also incur a performance penalty, we will use standard attribute selectors to target these parts. Notice how we can utilize the state attributes, like focused and invalid, to modify the styles of the parts. We hope to be future-proof with this decision, and updating styles and themes could be a simple find-and-replace of the selectors, if the proposed selectors are implemented as a standard.
  16. HOW TO PIERCE SHADOW DOM? But you are probably already

    wondering, how are those selectors going to match our elements with shadow DOMs?
  17. UTILIZE POLYMER STYLE MODULES Because we are using Polymer, we

    chose to use the style module mechanism Polymer offers out of the box, since it will be quite familiar to Polymer developers already.
  18. <dom-module id="vaadin-text-field-default-theme" theme-for="vaadin-text-field"> <template> <style> [part="label"] { font-size: 0.875em; font-weight:

    600; margin-bottom: 0.25em; } [part="input-field"] { border: 1px solid rgba(0, 0, 0, 0.3); background-color: #fff; - We create a normal Polymer style module by wrapping our style element with a dom-module and template elements. - Then, we introduce a new “theme-for” attribute for it, which specifies the element for which these styles should be included in. - It’s basically inverting the control for injecting styles to Polymer element templates. Normally you explicitly include styles in the elements template, but with this attribute and our ThemableMixin ES class, this style module will automatically be included to the template of vaadin-text-field.
  19. <template> <!-- Automatically injected --> <style include="vaadin-text-field-default-theme"></style> <div class="container"> <label

    part="label" ... >[[label]]</label> <div part="input-field"> <slot name="prefix"></slot> <input part="value" ... > <slot name="suffix"></slot> </div> <div part="error-message" ... >[[errorMessage]]</div> </div> </template> <VAADIN-TEXT-FIELD> Under the hood, it basically does this for the vaadin-text-field element, including the style module automatically for you.
  20. THEME MODULES ARE ALWAYS GLOBAL CAVEAT #1 Now, that applies

    to all instances of vaadin-text-field, no matter where you place them in your app. You always include these “theme modules” on the global level of your app, they can’t be scoped only to certain parts of your app.
  21. HOW TO SCOPE STYLES? If you need to scope the

    styles for some reason, there are two ways to do that.
  22. <dom-module id="my-text-field-theme" theme-for="vaadin-text-field"> <template> <style> [part="input-field"] { background-color: var(--input-field-background-color, #fff);

    } </style> </template> </dom-module> 1, EXPOSE CUSTOM PROPERTIES The first one is to expose custom properties from your theme modules, like the input-field-background-color here…
  23. <custom-style> <style> .some-part-of-my-app vaadin-text-field { --input-field-background-color: #eee; } </style> </custom-style>

    <div class="some-part-of-my-app"> <vaadin-text-field></vaadin-text-field> </div> 1. EXPOSE CUSTOM PROPERTIES …which you can then affect normally together with CSS selectors anywhere you need to. Just remember to use the custom-style element if you need to support IE 11 and Edge 14. I recommend this approach as your first option. If you end up exposing more than a handful of properties, though, then I suggest the following…
  24. <dom-module id="my-text-field-theme" theme-for="vaadin-text-field"> <template> <style> :host(.special-field) [part="input-field"] { background-color: #000;

    color: #fff; border: 2px solid #fff; border-radius: 9px; ... } </style> </template> </dom-module> 2. SCOPING SELECTORS …which is to include scoping selectors in the theme module directly, for the host element.
  25. <div> <vaadin-text-field class="special-field"> </vaadin-text-field> </div> 2. SCOPING SELECTORS Then you

    can just apply those scoping classes wherever you need them in your app, as those are also now globally available to all instances of the element. The downside of this approach is of course that you end up bloating the styles for all instances, even though only some of them need those styles.
  26. IMPORT THEME MODULES BEFORE THE ELEMENT CAVEAT #2 In order

    for the ThemableMixin to pickup the theme modules, they need to be imported before the element itself is imported and registered. That’s just how it is, no workarounds at the moment.
  27. <!-- Works --> <link rel="import" href="theme.html"> <link rel="import" href="element.html"> <!--

    Doesn’t work --> <link rel="import" href="element.html"> <link rel="import" href="theme.html"> Then, once you’ve installed our themes package, you can import individual element themes in your app to change their appearance. Here, we’re importing our Material theme which adapts vaadin-text-field to look pretty much just like paper-input. Just better, updated to follow the latest Material design specs.
  28. TARGET DOCUMENTED PARTS AND STATES ONLY CAVEAT #3 Target only

    the documented parts and state attributes, which are part of the public styling API, in your selectors. Other selectors are unsupported and can break in any future release.
  29. <dom-module id="my-text-field-theme" theme-for="vaadin-text-field"> <template> <style> .container { ... } </style>

    </template> </dom-module> UNSUPPORTED SELECTOR Even though you can do this, don’t be surprised if it breaks unexpectedly. As a general rule, your selectors should only target the host or part elements and one of their documented states. And if the shadow parts standard ever becomes available, and we drop support for these theme modules, these selectors can’t be replaced with the standard syntax.
  30. <template> <div class="container"> <label part="label" ... >[[label]]</label> <div part="input-field"> <slot

    name="prefix"></slot> <input part="value" ... > <slot name="suffix"></slot> </div> <div part="error-message" ... >[[errorMessage]]</div> </div> </template> IMPLEMENTATION DETAILS CAN CHANGE This also means that you should not rely on the element types that are used for the parts. Even though it is unlikely, those might also change because of some implementation issue requires it. As an example, the internal native input element of vaadin-text-field…
  31. <template> <div class="container"> <label part="label" ... >[[label]]</label> <div part="input-field"> <slot

    name="prefix"></slot> <div contenteditable="true" part="value" ... ></div> <slot name="suffix"></slot> </div> <div part="error-message" ... >[[errorMessage]]</div> </div> </template> IMPLEMENTATION DETAILS CAN CHANGE … could perhaps change to an editable div, while the stylable part stays the same.
  32. USING READY-MADE THEMES Alright, moving on. Now that we have

    the technical foundation covered, let’s take a look at something more directly useful to you – ready-made themes that we have for our elements.
  33. Material VALO We are building two themes for all of

    our elements: - Material theme, to allow you to continue using our elements together with Paper elements, - and Valo, our own visual design, evolved from the existing Vaadin Framework Valo theme.
  34. <link rel="import" href="vaadin-text-field/ vaadin-text-field.html"> <vaadin-text-field label="Label"> </vaadin-text-field> By default, our

    elements look really generic with minimal styling. That is the recommended approach for all web components, as documented in The Gold Standard Checklist for Web Components. So, when just importing vaadin-text-field, you get something like this. Note, that the font is inherited from the main document.
  35. <link rel="import" href="vaadin-themes/material/ vaadin-text-field.html"> <link rel="import" href="vaadin-text-field/ vaadin-text-field.html"> <vaadin-text-field label="Label">

    </vaadin-text-field> Then, once you’ve installed our themes package, you can import individual element themes in your app to change their appearance. Here, we’re importing our Material theme which adapts vaadin-text-field to look pretty much just like paper-input. Just better, updated to follow the latest Material design specs.
  36. <link rel="import" href="vaadin-themes/valo/ vaadin-text-field.html"> <link rel="import" href="vaadin-text-field/ vaadin-text-field.html"> <vaadin-text-field label="Label">

    </vaadin-text-field> Then, just by changing the name of the theme you import, you can adapt another look and feel for the element.
  37. Here’s our date picker as an example as well. With

    the default styles, it’s still perfectly usable, but quite generic as well.
  38. The Material theme updates many small details, such as icons,

    fonts, shadows, and adapts the embedded vaadin-text-field and vaadin-button elements to the same theme as well.
  39. Both of our themes also have a dark variation built-in,

    so you can easily change the colors to fit a dark environment.
  40. BUILT-IN VARIATIONS Now, about these variations. The dark variations are

    not the only ones available, but there are other kinds of element specific additional styles available.
  41. <vaadin-button theme="primary large"> Button </vaadin-button> But by adding a couple

    of theme variants, you get a bigger and and more prominent button. We chose to use a custom “theme” attribute instead of the standard class attribute to avoid unwanted naming collisions and to keep the names of these variants short.
  42. REFERENCE APP STARTER We are building a reference app using

    the Valo theme, responsive out-of-the-box, to help you get started and see how to use our elements and our themes the way we intended. It’s an imaginary bakery storefront app to track orders, but the idea is that you can easily refactor it for your use case.
  43. The dark version is as easy as adding the theme=“dark”

    attribute for the main element of your app. We are also creating a tool that allows you to adjust the Valo theme dynamically right inside the app, so you can immediately see the changes live.
  44. Here’s are two quick examples of how you can change

    the colors, fonts and roundedness of the app for example.
  45. These are the first things that we have customizable in

    the theme right now, but more easily customizable parameters are planned, to control sizing and spacing. You can ask any Vaadin guys around here to give you a live demo after the talk.
  46. NOT YET STABLE SUBJECT TO CHANGE FINAL CAVEAT FIRST STABLE

    RELEASE COMING THIS YEAR Lastly, this is still pretty early work, and we aren’t promising this is the final way how our themes are going to work. We expect the technical implementation and theming concepts to settle down during September, after which you can start using it more for real. We aim to have the first stable version of the themes ready by the end of the year.
  47. FIGHT FOR SIMPLICITY As our motto, we always fight for

    simplicity in everything we do, and I encourage you to do the same