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

You might not need a CSS preprocessor

You might not need a CSS preprocessor

📹 Video: https://www.youtube.com/watch?v=o2an6ON6i3g
🔥 Live slides + Demos: https://blog.hospodarets.com/you-might-not-need-a-css-preprocessor

Presented at Topconf Tallinn 2016.

Having native CSS variables, which work in all modern browsers, also mixins and nesting, we can consider switching from preprocessors to native CSS and look for a path to migration.

In the presentation, there is a description of the modern abilities of CSS, which specs are in progress, demos how this already works in browsers and how to use polyfills to make these CSS goodness work everywhere.

You will know:
1) That mostly everything you need in preprocessors currently is available in pure CSS
2) How to make it work cross-browser
3) The real examples of already migrated sites

Serg Hospodarets

September 15, 2016
Tweet

More Decks by Serg Hospodarets

Other Decks in Programming

Transcript

  1. You might not need a
    CSS preprocessor
    DevDay 2016
    Serg Hospodarets
    @malyw

    View full-size slide

  2. 1996 year - CSS invented

    View full-size slide

  3. Content and presentation are separated
    .page-header {
    /* shorthand */
    border-bottom: 1px solid #eee;
    }
    p {
    font-family: Arial; /* font */
    font-size: 14px; /* font */
    color: #333; /* color */
    margin: 10px 0; /* layout */
    }
    .btn-outline {
    color: #563d7c; /* color */
    border-color: #563d7c; /* color */
    }

    View full-size slide

  4. CSS problems
    Absence of variables
    Absence of mixins
    Modules
    Nested rules are not
    supported
    Code duplication as result

    View full-size slide

  5. Preprocessors time

    View full-size slide

  6. Solved by
    preprocessors

    View full-size slide

  7. Sass:
    Variables and Operators (+, -, *, /, %)
    $font-size: 10px;
    $font-family: Helvetica, sans-serif;
    body {
    font: $font-size $font-family;
    }
    .mark{
    font-size: 1.5 * $font-size;
    }

    View full-size slide

  8. Mixins
    @mixin clearfix {
    &:after {
    display: block;
    content: '';
    clear: both;
    }
    }
    .sidebar{
    @include clearfix;
    }
    .main{
    @include clearfix;
    }
    .sidebar:after {
    display: block;
    content: '';
    clear: both;
    }
    .main:after {
    display: block;
    content: '';
    clear: both;
    }

    View full-size slide

  9. Nesting
    HTML SCSS



    link



    dropdown link




    // menu
    .nav {
    > li {
    > a:hover {
    background-color: red;
    }
    // submenu
    > ul {
    background-color: #fff;
    > li > a:hover {
    background-color: black;
    }
    }
    }
    }

    View full-size slide

  10. What we use today

    View full-size slide

  11. Preprocessors
    problems

    View full-size slide

  12. Additional setup is needed to make a compiler work
    Any change require recompilation
    Compilation takes time ⏰

    View full-size slide

  13. Each has own syntax!
    // Sass
    $color: #f00;
    $images: "../img";
    @mixin clearfix {
    &:after {
    content: " ";
    display: block;
    clear: both;
    }
    }
    body {
    color: $color;
    background: url("#{img}/1.png");
    @include clearfix;
    }
    // Less
    @color: #f00;
    @images: "../img";
    .clearfix() {
    &:after {
    content: " ";
    display: block;
    clear: both;
    }
    }
    body {
    color: @color;
    background: url("@{img}/1.png");
    .clearfix;
    }

    View full-size slide

  14. Source maps are required
    Debug might be hard (or buggy)

    View full-size slide

  15. We want
    Variables
    Mixins
    Nesting
    Modules
    Selector helpers, color functions
    We don't want
    Additional setup
    Compilation
    Not standardized syntax
    Hard debug

    View full-size slide

  16. Does CSS have all this
    today?
    Let's take a look

    View full-size slide

  17. What we want?
    Variables

    View full-size slide

  18. First CSS variable currentColor
    :root { color: red; }
    i {border: 1px solid currentColor;}
    :root { color: red; }
    i {border: 1px solid red;}
    Other "variables": em, rem
    html { font-size: 1em; }
    h1 { font-size: 2.074em; }
    @media (min-width: 1400px) {
    html { font-size: 1.25em; }
    }
    html { font-size: 16px; }
    h1 { font-size: 33px; }
    @media (min-width: 1400px) {
    html { font-size: 20px; }
    h1 { font-size: 41px; }
    }

    View full-size slide

  19. (a.k.a. CSS variables)
    CSS custom properties

    View full-size slide

  20. Syntax
    /* declaration */
    --VAR_NAME: ;
    /* usage */
    var(--VAR_NAME)
    /* root element selector (global scope), e.g. */
    :root {
    /* CSS variables declarations */
    --main-color: #ff00ff;
    --main-bg: rgb(200, 255, 255);
    }
    body {
    /* use the variable */
    color: var(--main-color);
    }
    Didn't expect the "--"? There was .
    a reason

    View full-size slide

  21. Variable examples
    :root{
    --main-color: #4d4e53;
    --main-bg: rgb(255, 255, 255);
    --logo-border-color: rebeccapurple;
    --header-height: 68px;
    --content-padding: 10px 20px;
    --base-line-height: 1.428571429;
    --transition-duration: .35s;
    --external-link: "external link";
    --margin-top: calc(2vh + 20px);
    }
    And even..
    :root{
    --foo: if(x > 5) this.width = 10;
    }

    View full-size slide

  22. Variable Defaults
    If the variable has already been assigned to
    - it won’t be re-assigned
    p {
    --p-margin: 5px;
    margin: var(--p-margin, 0 0 10px); /* 5px */
    }
    If it doesn’t have a value yet
    - it will be given one.
    p {
    margin: var(--p-margin, 0 0 10px);/* 0 0 10px */
    }

    View full-size slide

  23. Reassign vars from others
    .block {
    --block-text: 'This is my block';
    --block-highlight-text: var(--block-text)' with highlight';
    }
    .block__highlight:before {
    content: var(--block-highlight-text);
    /*This is my block with highlight*/
    }

    View full-size slide

  24. Reset/inherit values
    As for any other CSS property, you can apply "initial" and
    "inherit" values
    .with-reset {
    --bgcolor: initial;/* RESETS THE VALUE for the scope */
    --color: green;/* CHANGES THE VALUE */
    --border: inherit;/* INHERITS THE VALUE for the scope */
    }

    View full-size slide

  25. Usage example: emulating non existing CSS rule

    View full-size slide

  26. Scopes

    My block is
    awesome

    Follow usual CSS cascade rules:
    :root{
    --global-var: 1em;
    /* --global-var is available globally */
    }
    .block {
    --block-var: 1.5em;
    /* --global-var and --block-var are available */
    }
    .block__highlight {
    --block-highlight-var: 2rem;
    /* --global-var , --block-var and --block-highlight-var */
    font-size: var(--block-highlight-font-size);
    }

    View full-size slide

  27. CSS / preprocessors scopes are different
    /* SCSS: scope depends on
    the selectors structure
    in the code */
    $font-size: 20px;
    .block{
    $font-size: 42px;
    }
    .block__highlight{
    font-size: $font-size;
    }
    /* CSS: scope depends
    on the selectors structure
    in the DOM */
    :root{
    --font-size: 20px;
    }
    .block{
    --font-size: 42px;
    }
    .block__highlight{
    font-size: var(--font-size);
    }

    View full-size slide

  28. Scope examples
    /* Global scope (usually

    View full-size slide

  29. Variables are alive

    View full-size slide

  30. Operators and calculations
    :root {
    --block-font-size: 1rem;
    }
    .block__highlight {
    /* DOESN'T WORK */
    font-size: var(--block-font-size)*1.5;
    }
    CSS calc( ) to the rescue (for values)!
    :root {
    --block-font-size: 1rem;
    }
    .block__highlight {
    /* WORKS */
    font-size: calc(var(--block-font-size)*1.5);
    }

    View full-size slide

  31. Generate colors from CSS custom properties

    View full-size slide

  32. CSS to JS: without
    To pass variables from CSS to JS we used to use
    to write JSON in CSS
    workarounds or hacks
    .breakpoints-data {
    font-family: '{"phone":"480px","tablet":"800px"}';
    }

    View full-size slide

  33. CSS to JS: with
    .breakpoints-data {
    --phone: 480px;
    --tablet: 800px;
    }
    JS
    const breakpointsData = document.querySelector('.breakpoints-data');
    // GET
    const phone = getComputedStyle(breakpointsData)
    .getPropertyValue('--phone');
    // SET
    breakpointsData.style
    .setProperty('--phone', 'custom');

    View full-size slide

  34. Check if supported
    CSS
    @supports ( (--a: 0)) {
    /* supported */
    }
    @supports ( not (--a: 0)) {
    /* not supported */
    }
    JS
    const isSupported = window.CSS && window.CSS.supports &&
    window.CSS.supports('--a', 0);
    /* e.g. load a CSS file generated by a preprocessor */
    if(!isSupported){
    removeCss('css-custom-properties.css')
    loadCss('without-css-custom-properties.css');
    }

    View full-size slide

  35. Why else CSS Custom Props are
    better?

    View full-size slide

  36. Color schema switcher based on CSS custom property
    values
    Cannot be done by a preprocessor without generating additional code

    View full-size slide

  37. What do we want?
    Mixins

    View full-size slide

  38. (a.k.a. CSS mixins)
    CSS Custom Sets of Properties and
    @apply rule

    View full-size slide

  39. Custom Property:
    :root{
    /* --property: value; */
    --VAR: ;
    }
    Custom properties can hold more than just values- they can
    also be used to hold sets of declarations:
    :root{
    /*
    --property: {
    property1: value1;
    property...: value...;
    }
    */
    --MIXIN: {
    /* style declaration 1 */
    /* style declaration ... */
    };
    }

    View full-size slide

  40. Syntax
    :root {
    --pink-schema: {
    color: #6A8759;
    background-color: #F64778;
    }
    }
    body{
    @apply --pink-schema;
    }
    @apply rule takes these sets of declarations and inlines
    them in another style rule

    View full-size slide

  41. Reasons to use
    Put reusable bunches of styles into separate entities
    Avoid code duplication
    Apply changes in a central place
    Behavior
    Everything form CSS variables (scopes, usage from JS etc.) is
    applicable for Custom Sets of Properties

    View full-size slide

  42. Examples
    :root {
    --clearfix: {
    display: table;
    clear: both;
    content: '';
    };
    }
    .clearfix:after{
    @apply --clearfix;
    }
    :root {
    --overflow-ellipsis: {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    };
    }
    .overflow-box{
    @apply --overflow-ellipsis;
    }

    View full-size slide

  43. (try in Chrome Canary)
    CSS Triangle Mixin

    View full-size slide

  44. Problem- variables cannot be passed
    :root {
    --triangle-to-bottom-size: 50px;
    --triangle-to-bottom: {
    /* STYLES */
    border-bottom-width: var(--triangle-to-bottom-size);
    };
    }
    .triangle-to-bottom {
    --triangle-to-bottom-size: 8px;
    @apply --triangle-to-bottom;
    /* but still 50px size is applied for border-bottom-width */
    }
    Discussion to change this behavior is in progress

    View full-size slide

  45. Mixins for CSS vendor prefixes?
    Every time you need clip-path:
    Lea Verou's idea:
    * {/* has zero specificity */
    /* prevents the property inheritance from outer scopes */
    --clip-path: initial;
    -webkit-clip-path: var(--clip-path);
    clip-path: var(--clip-path);
    }
    header {/* any selector like this overrides the "*" */
    /* assign the prop value for the scope */
    --clip-path: polygon(0% 0%, 100% 0%, 100% 100%%, 0% 100%);
    }

    View full-size slide

  46. What we need?
    Nesting

    View full-size slide

  47. What we want to avoid?
    CSS code duplication in selectors
    Bad readability of the code
    table.colortable td {
    text-align:center;
    }
    table.colortable td.upper {
    text-transform:uppercase;
    }
    table.colortable td:first-child,
    table.colortable td:first-child+td {
    border:1px solid #000;
    }

    View full-size slide

  48. Tab Atkins' CSS Nesting spec
    proposal
    Syntax is close to preprocessors
    /* Dropdown menu on hover */
    ul {
    /* direct nesting (& MUST be the first part of selector)*/
    & > li {
    color: #000;
    & > ul { display: none; }
    &:hover {
    color: #f00;
    & > ul { display: block; }
    }
    }
    }

    View full-size slide

  49. The Nesting At-Rule: '@nest' for complex cases
    @nest < selector (MUST CONTAIN a nesting selector '&') >
    .foo {
    color: black;
    @nest body.loading & {
    opacity: 0.5;
    }
    @nest :not(&) {
    color: white;
    }
    }
    .foo {
    color: black;
    }
    body.loading .foo {
    opacity: 0.5;
    }
    :not(.foo) {
    color: white;
    }

    View full-size slide

  50. Migration is easy
    Nesting selector syntax is very close to Sass
    Complex nesting can be done using @nest at-rule
    media expression etc. are nested:
    a {
    @media (min-width: 30em) {
    color: yellow;
    }
    }
    @media (min-width: 30em) {
    a {
    color: yellow
    }
    }

    View full-size slide

  51. What we wish?
    Modules

    View full-size slide

  52. The @import CSS at-rule is used to import style rules from
    other style sheets.
    It is available in all browsers since IE 5.5!

    View full-size slide

  53. Why we didn't use this before?
    Bugs in old browsers with the order of inclusion
    Before requests didn't go in parallel
    For HTTP1x good practice is file concatenation.
    With coming of HTTP2 rules will be cnanged.

    View full-size slide

  54. You can easily apply media queries for different stylesheets.
    Advantages for free:
    Conditional loading
    /* Formal syntax */
    @import [ | ] []?;
    @import url("print.css") print;
    @import "mobile.css" (max-width: 728px);
    The linked resources are loaded only when condition is met.

    View full-size slide

  55. We have
    Variables, Mixins
    Nesting and Modules
    What else we want?
    Selector helpers for complex cases
    Color functions

    View full-size slide

  56. pseudo-class
    :matches
    /* SYNTAX */
    :matches( selector[, selector]* )
    A functional pseudo-class taking a selector list as its
    argument.
    .nav:matches(.side,.top) .links:matches(:hover, :focus) {
    color: #BADA55;
    }
    /* Same thing as this... */
    .nav.side .links:hover,
    .nav.top .links:hover,
    .nav.side .links:focus,
    .nav.top .links:focus {
    color: #BADA55;
    }

    View full-size slide

  57. @custom-selector
    /* SYNTAX */
    @custom-selector: ;
    Example:
    @custom-selector :--text-inputs input[type="text"],
    input[type="password"];
    :--text-inputs.disabled,
    :--text-inputs[disabled] {
    opacity: 0.5
    }
    Same as:
    input[type="text"].disabled,
    input[type="password"].disabled,
    input[type="text"][disabled],
    input[type="password"][disabled] {
    opacity: 0.5
    }

    View full-size slide

  58. Color functions
    /* SYNTAX */
    color( * )
    some adjusters have shortcuts
    adjusters can be pipped
    color(
    red /* from red */
    blackness(+25%) /* to 25% more black than red */
    blackness(+25%) /* to 50% more black than red */
    blackness(-50%) /* to red again */
    hue(+ 30deg) /* to orange */
    hue(- 30deg) /* to red again */
    );

    View full-size slide

  59. Better media queries!
    Problem
    The syntax is too long and they cannot be reused easily
    Need
    A simple way to set breakpoints and reuse them

    View full-size slide

  60. Media Queries Level 4:
    Custom media queries
    /* SYNTAX */
    @custom-media --NAME ;
    How to use:
    @custom-media --tablet (min-width: 800px) and (max-width: 1024px);
    @media (--tablet){
    .custom-media-queries{
    background-color: red;
    }
    }

    View full-size slide

  61. Instead of:
    Use:
    Media queries ranges
    @media (min-width: 800px) and (max-width: 1024px) {
    .media-queries-range{
    background-color: red;
    }
    }
    @media (width >= 800px) and (width <= 1024px) {
    .media-queries-range{ background-color: red; }
    }
    With custom media queries:
    @custom-media --tablet (width >= 800px) and (width <= 1024px);
    @media (--tablet){
    /* STYLES */
    }

    View full-size slide

  62. Can we Use it now?
    Current situation
    CSS variables are supported in all the modern browsers
    except EDGE
    CSS mixins work in Chrome Canary
    Some of other specs are integrated in various browsers,
    sometimes it's in beta/dev versions
    Should we wait for another couple years?

    View full-size slide

  63. PostCSS based
    Includes other PostCSS plugins (vars, mixins etc.)
    If you already use Autoprefixer- change is straign forward
    Build setup:
    //...
    postcss: {
    options: {
    processors: [
    require('autoprefixer')({
    browsers: ['last 2 versions']
    })
    ]
    },
    //...
    //...
    postcss: {
    options: {
    processors: [
    require('postcss-cssnext')({
    browsers: ['last 2 versions']
    })
    ]
    },
    //...

    View full-size slide

  64. How to migrate
    1) Preprocessor
    Add PostCSS and cssnext
    2) Preprocessor+PostCSS+ability to use new CSS features
    Change vars, mixins, media queries, colors and selectors to the CSS ones
    Disable a preprocessor
    3) PostCSS + all the CSS additions power
    Disable cssnext and PostCss when the browsers support everything
    4) You have pure CSS with everything

    View full-size slide

  65. You still can combine preprocessor and all the mentioned
    CSS additions (or process them via PostCSS) to use the
    strongest parts of the both.

    View full-size slide

  66. Any live sites which use all these
    new CSS features?
    blog.hospodarets.com

    View full-size slide

  67. Conclusions
    Today we have mostly everything we need from
    preprocessors in pure CSS
    variables, mixins
    modules, nesting
    complex selectors and color functions
    We have polyfills to make it work until it's supported in all
    browsers
    We have the real examples of migrated applications

    View full-size slide

  68. How can I help / stay tuned?
    Spec, dra s, proposals
    CSS Working Group (WG) Editor Dra s
    Tab Atkins spec proposals
    Other
    Subscribe to CSS Working Group mailing list / RSS etc.
    cssnext issues

    View full-size slide

  69. Thank you!
    @malyw

    View full-size slide