From 11ee5e99ec2bffe30c7be5cf482f437ffd564e28 Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Tue, 7 May 2019 15:25:38 +0200 Subject: [PATCH] chore(docs): core docs about style components --- CONTRIBUTING.md | 1 + README.md | 4 + docs/README.md | 5 + docs/definitions.md | 52 ++++++++++ docs/guidelinesStyling.md | 205 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/definitions.md create mode 100644 docs/guidelinesStyling.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42270324b..198b5ced9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ git clone https://github.com/ing-bank/lion.git cd lion # Install dependencies +# We require yarn as we use yarn workspaces yarn install # Create a branch for your changes diff --git a/README.md b/README.md index 764335619..0b04becd8 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,10 @@ context, supports many edge cases and is accessible in all relevant screen reade Lion aims to do the heavy lifting for you. This means you only have to apply your own Design System: by delivering styles, configuring components and adding a minimal set of custom logic on top. +## Coding guidlines + +Check out our [coding guidelines](./docs/README.md) for more detailed information. + ## How to contribute Lion Web Components are only as good as its contributions. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..89875bf2d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +# Coding Guidelines + +First be sure to understand our [definitions](./definitions.md). + +- [Guidelines for Styling](./guidelinesStyling.md) diff --git a/docs/definitions.md b/docs/definitions.md new file mode 100644 index 000000000..81bd921fc --- /dev/null +++ b/docs/definitions.md @@ -0,0 +1,52 @@ +# Definitions and terms + +Below you will find a list of definitions and terms that will be used throughout our code +documentation. + +- `Application Developer`: + Developers consuming our webcomponents inside an application (not extending them). + Application Developers are only allowed to interact with `public` properties and methods. + Can be abbreviated as `AD`. Sometimes also called `Consuming Developer`. + +- `Subclasser`: + Developers extending our webcomponents. For instance: `MaterialInput extends LionInput`. + Subclassers have access to protected methods (prefixed with an underscore or marked as protected), + but not to private methods. + +- `public`: + Methods and properties are public when they are not prefixed by an underscore. + They can be used by Application Developers. + + ```js + class SoccerPlayer { + kickBall() { + // Soccer player can kick a ball + } + } + ``` + +- `protected`: + Methods and properties are protected when they contain one underscore or are explicitly marked as + protected in the code. + They can be used by Subclassers. + + ```js + class SoccerPlayer { + _catchBall() { + // Soccer player usually do not need to catch a ball (with it's hands) + } + } + ``` + +- `private`: + Methods and properties are protected when they contain two underscores or are explicitly marked as + private in the code. + They can be used within the class where they are defined (developers of Lion components). + + ```js + class SoccerPlayer { + __score() { + // internally save how many goals have been made + } + } + ``` diff --git a/docs/guidelinesStyling.md b/docs/guidelinesStyling.md new file mode 100644 index 000000000..77ceaf519 --- /dev/null +++ b/docs/guidelinesStyling.md @@ -0,0 +1,205 @@ +# Guidlines for Styling + +## Markup and styling + +All Lion webcomponents have white label styling: this means theming is not applied, +but functional styling is. + +### Functional styling + +Functional styling can be divided into defaults for 'basic layout' and 'accessibility'. +Examples for both categories can be found below: + +- Basic layout examples: + + - A dropdown menu has 'position: absolute' + - A lion-button might behave as an 'display: inline-block' element + - A suffix attached to an input is horizontally positioned (instead of vertically stacked) + +- Accessibility examples: + - content (for instance a caption in a table) can be 'visually hidden': it will be + readable by screen readers, but invisible for end users + - when an html table is used, we allow [subclassers](./definitions.md) to override the display + property ('table-cell') to 'flex'. By putting the proper accessible roles ('role="cell"') in the + markup, we guaruantee our markup stays accessible + +Although Lion components try to stay as unbiased as possible with regard to styling, defaults will +be needed. In these cases we try to follow the platform as much as possible. If the platform +doesn't provide defaults, the largest common denominator across exisiting UI frameworks is taken as +a lead. + +## Style components + +A style component consists of a set of css classes mapping to a certain html structure. Although css +components are often implemented by webcomponents, they should be considered modules on their own, +reusable in different contexts: they should be considered the lowest abstraction layer of a +webcomponent. + +A webcomponent usually implements a css module (mapped at host level), but the opposite is not +necessarily true: a css module doesn't have to be used inside a webcomponent or at the root level +of a webcomponent. + +Advantages of developing style components isolated from webcomponents are: + +- They can be reused in non shadow dom contexts. +- Not everything has to be a webcomponent: anchor or card styling inside a webcomponent would be a + matter of importing a style component into a shadowroot. + +### Requirements + +Style components are written with the following assumptions in mind: + +- **Environment agnostic**: Lion webcomponents are unopinionated about their environment: they + should be usable in every context/framework/technology thinkable, regardless of whether shadow dom + is used. +- **Customisable**: They should provide an api for [subclassers](./definitions.md): + - **white label**: components (just like webcomponents) are white label components. They provide + a flexible html structure which should be mappable to any Design System. + This approach is inspired by [Inuitcss](https://github.com/inuitcss/inuitcss) (an architectural + foundation for white label style components) and [Bootstrap Material](https://fezvrasta.github.io/bootstrap-material-design/) + (a Material Design system based on existing Bootstrap html/class names). + This is also [a great example of a Design System](https://www.carbondesignsystem.com/components/checkbox/code) + taking this(style components as lowest abstraction layer) approach. + - **well structured**: for clear, readable and maintainable code we use [BEM](http://getbem.com/): + it forces the + developer to think about component structure in terms of 'block', 'element' and 'modifier' + parts/roles. The classes defined in a BEM component form the api for a + [subclasser](./definitions.md): he can 'fill in' the BEM selectors by overriding them in the + `static get styles()` configuration property on the constructor of a webcomponent. + - **flexible markup**: By making the HTML structure purposely 'loose', multiple Design Systems can + be made compatible with the style component. When the default html/css structure is not flexible + enough, it can be extended by creating new BEM element selectors. + +## Why BEM? + +For css class naming, we use [BEM](http://getbem.com/) naming conventions. This helps you identify +semantics of your markup at a quick glance, resulting in more readable and maintainable code. +BEM provides us the following advantages: + +- **Clear namings** that convey meaning about semantics and structure at a quick glance +- **Prevent collisions** within shadow roots. Larger shadow roots using multiple style components + would easily collide when css selectors would be scoped to the shadow dom in which they were + created. For instance, calling the header of a card '.header' instead of '.c-card\_\_header' + would make the card component not reusable in different contexts. +- **Maximum flexibility** we can write css regardless of context. When we create a style component + for a card, we can offer it as a style component only and later decide to offer it as a webcomponent. + Or offer the style component + as a more lower level api for advanced use cases and the webcomponent (that has limiting markup and + styling options) for the majority of cases. + Also, when we already have a webcomponent for the card and people want to create a custom card + component, it might make more sense to write a new webcomponent instead of extending the existing + card webcomponent. + Having style components as described above, allows for maximum flexibility in these scenarios. + Future CSS updates can then be done from a central place, without having to rewrite forked webcomponents. +- **Performance** we aim for applications having a limited number of shadow roots and we consider + shadow root creation a performance concern. Style components reduce the need for shadow roots. + + +### Challenges with BEM in shadow dom + +Mapping BEM components to shadow root is not really straightforward: + +1. host styles should be rewritten to `:host {}` instead of `.my-block {}` +2. slot styles be rewritten to `::slotted(.my-block__element)` instead of `.my-block__element` +3. we should not [self-apply classes](https://developers.google.com/web/fundamentals/web-components/best-practices) + on host level. Apart from this being a bad practice, it will trigger linting errors in our setup. + +### Mapping host and slot styles + +The [CSS Module](https://github.com/w3c/webcomponents/issues/759) and [CSS Selector](https://github.com/w3c/csswg-drafts/issues/3714) proposals would allow us to reuse BEM components more easily within webcomponents. +We might also think about other ways of creating build steps that would allow us to map BEM +components to a shadow root, making it possible to map hosts and slots in an eay way. + + + +### No self-application of classes + +In order to adhere to this rule, notation of our BEM modifiers that live on the host (which are +ususally written like `.my-block--my-modifier`) will be rewritten to `.my-block[my-modifier]` +Although this would only be needed for the third challenge as described above, for consistency, +we apply it to all our modifiers within our style component. +So for instance `.my-block__element--my-other-modifier` becomes +`.my-block__element[my-other-modifier]`. + +### Why mapping style components to the host matters + +- **flexible host overrides**: + Imagine we have a style component `.c-alert` and we want to map this to a webcomponent ``. + In our Design system it might be a good practice to give all our block components a default bottom + margin of 16px. An [Application Developer](./definitions.md) might need an instance of `` where he wants to + set this bottom margin to 0. + If the margin-bottom of 16px would not be defined on the host level but one level deeper (the first + div element within the shadow root of ``), the Application Developer needs to apply a + negative margin of -16px. The latter would be a bad practice. + +- **easily map modifiers**: + If we continue with our alert example and we have a flag 'is-closeable', we might need to adjust + styling based on that. In this case we assume the attribute 'is-closeable' serves as a styling + hook for the `.c-alert` component. + +## Css variables + +Css variables will not be added in our white label style components, but adding them in your own +extension layer would be a perfect fit. + +## Parts and themes + +The `::part` and `::theme` specs might currently not be widely adopted enough to be used inside +style components. When they are, we could be consider to add them later to our components as a means +to theme components (changing a whole Design System is not a good idea). + + + +