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

Ebea51dd17c435982c03bc9ae29dc3a6?s=128

Serg Hospodarets

September 15, 2016
Tweet

Transcript

  1. 2.
  2. 5.

    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 */ }
  3. 6.

    CSS problems Absence of variables Absence of mixins Modules Nested

    rules are not supported Code duplication as result
  4. 9.

    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; }
  5. 10.

    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; }
  6. 11.

    Nesting HTML SCSS <!-- menu --> <div class="nav"> <li> <a

    href="">link</a> <!-- submenu --> <ul> <li> <a href="">dropdown link</a> </li> </ul> </li> </div> // menu .nav { > li { > a:hover { background-color: red; } // submenu > ul { background-color: #fff; > li > a:hover { background-color: black; } } } }
  7. 12.
  8. 15.

    Additional setup is needed to make a compiler work Any

    change require recompilation Compilation takes time ⏰
  9. 16.

    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; }
  10. 18.

    We want Variables Mixins Nesting Modules Selector helpers, color functions

    We don't want Additional setup Compilation Not standardized syntax Hard debug
  11. 21.

    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; } }
  12. 23.
  13. 25.

    Syntax /* declaration */ --VAR_NAME: <declaration-value>; /* usage */ var(--VAR_NAME)

    /* root element selector (global scope), e.g. <html> */ :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
  14. 26.

    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; }
  15. 27.

    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 */ }
  16. 28.

    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*/ }
  17. 29.

    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 */ }
  18. 31.

    Scopes <div class="block"> My block is <div class="block block__highlight">awesome</div> </div>

    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); }
  19. 32.

    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); }
  20. 33.

    Scope examples /* Global scope (usually <html/>) */ :root {

    --bg: #f00; } /* var is reassigned when media query is applied */ @media screen and (min-width: 800px) { :root { --bg: #f00; } } /* and on body hover is reassigned for <body/> */ /* (but will be the same for <html/> as it's a parent scope) */ body:hover { --bg: #ff0; }
  21. 35.

    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); }
  22. 37.

    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"}'; }
  23. 38.

    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');
  24. 39.
  25. 40.

    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'); }
  26. 42.

    Color schema switcher based on CSS custom property values Cannot

    be done by a preprocessor without generating additional code
  27. 45.

    Custom Property: :root{ /* --property: value; */ --VAR: <declaration-value>; }

    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 ... */ }; }
  28. 46.

    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
  29. 47.

    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
  30. 48.

    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; }
  31. 50.

    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
  32. 51.

    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%); }
  33. 53.

    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; }
  34. 54.

    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; } } } }
  35. 55.

    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; }
  36. 56.

    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 } }
  37. 58.

    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!
  38. 59.
  39. 60.

    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.
  40. 61.

    You can easily apply media queries for different stylesheets. Advantages

    for free: Conditional loading /* Formal syntax */ @import [ <string> | <url> ] [<media-query-list>]?; @import url("print.css") print; @import "mobile.css" (max-width: 728px); The linked resources are loaded only when condition is met.
  41. 62.

    We have Variables, Mixins Nesting and Modules What else we

    want? Selector helpers for complex cases Color functions
  42. 63.

    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; }
  43. 64.

    @custom-selector /* SYNTAX */ @custom-selector: <custom-selector> <selector-list>; 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 }
  44. 65.

    Color functions /* SYNTAX */ color( <color> <color-adjuster>* ) 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 */ );
  45. 66.
  46. 67.

    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
  47. 68.

    Media Queries Level 4: Custom media queries /* SYNTAX */

    @custom-media --NAME <media-query-list>; How to use: @custom-media --tablet (min-width: 800px) and (max-width: 1024px); @media (--tablet){ .custom-media-queries{ background-color: red; } }
  48. 69.

    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 */ }
  49. 70.

    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?
  50. 71.
  51. 72.

    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'] }) ] }, //...
  52. 73.

    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
  53. 74.

    You still can combine preprocessor and all the mentioned CSS

    additions (or process them via PostCSS) to use the strongest parts of the both.
  54. 76.
  55. 77.

    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
  56. 78.

    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