Slide 1

Slide 1 text

Extending CSS with Event-Driven Virtual Stylesheets

Slide 2

Slide 2 text

Extending CSS with Event-Driven Virtual Stylesheets

Slide 3

Slide 3 text

“Extending CSS” • Adding new styling abilities • Doing things CSS can’t ‘polyfill’ using existing features • Leveraging JavaScript and built-in DOM APIs for styling • Styling beyond the scope of what CSS is designed to do

Slide 4

Slide 4 text

“Extending CSS” is NOT • Not replacing or reimplementing what CSS already does • Not just adding syntactic sugar to CSS (pre-processing) • Not necessarily violating the “Separation of Concerns”

Slide 5

Slide 5 text

Extending CSS with Event-Driven Virtual Stylesheets

Slide 6

Slide 6 text

“Event-Driven” • CSS is already event-driven (:hover, :focus, etc) • Style changes often mirror events happening in the browser (@media) • Style changes are often driven by events in Javascript

Slide 7

Slide 7 text

Extending CSS with Event-Driven Virtual Stylesheets

Slide 8

Slide 8 text

“Virtual Stylesheets” • Dynamic stylesheet that can change over time • Leverage JavaScript logic for templating • Leverage JavaScript for interpolating values • Can be located in DOM or CSSOM

Slide 9

Slide 9 text

Types of Virtual Stylesheet

Slide 10

Slide 10 text

Types of Virtual Stylesheet • tag in HTML • <script> or <template> in HTML • External <script> or <link> file • Any string in JavaScript

Slide 11

Slide 11 text

Places Where You Can
 Add Logic to Styles

Slide 12

Slide 12 text

Places Where You Can
 Add Logic to Styles • HTML: using custom attributes (for selector, test, event) • CSS: using custom attribute selectors or CSS variables • JS: using strings, variables, and functions

Slide 13

Slide 13 text

Useful Events for Styling

Slide 14

Slide 14 text

Useful Events for Styling • Global events: load, resize, input, click • Element-based events: scroll, mutation observer, resize observer • App-based events: JS framework updates, plugin callbacks, state-based styling logic in apps

Slide 15

Slide 15 text

Useful Tests for Styling

Slide 16

Slide 16 text

Useful Tests for Styling • el => el.offsetWidth < 500 // less than • el => el.offsetWidth <= 500 // less or equal (max) • el => el.offsetWidth == 500 // equal • el => el.offsetWidth >= 500 // greater or equal (min) • el => el.offsetWidth > 500 // greater than

Slide 17

Slide 17 text

Useful Properties for Testing • offsetWidth • offsetHeight • textContent.length || value.length • children.length • scrollTop • scrollLeft

Slide 18

Slide 18 text

The 3 Rules of
 Event-Driven Styling

Slide 19

Slide 19 text

Rule №1

Slide 20

Slide 20 text

Do everything in CSS that can be done in CSS

Slide 21

Slide 21 text

AKA: Let CSS be CSS,
 let selectors be selectors

Slide 22

Slide 22 text

Rule №2

Slide 23

Slide 23 text

Limit the number of tests being performed

Slide 24

Slide 24 text

Rule №3

Slide 25

Slide 25 text

Consolidate event listeners

Slide 26

Slide 26 text

Style Scoping

Slide 27

Slide 27 text

Style Scoping • Exists at 3 levels: stylesheet, selector, and property • 1 scoped stylesheets: @media & container queries • 2 scoped selectors: :has(), :hover, element queries • 3 scoped properties: CSS variables, JS interpolation

Slide 28

Slide 28 text

A Selected History of Scoped Style Proposals

Slide 29

Slide 29 text

Scoped Style Proposals • CSS first proposed in 1994 • JavaScript Style Sheets (JSSS) by Netscape in 1996 • Dynamic Properties in IE5-8 by Microsoft in 1999 • CSS @media queries proposed in 2001
 (standardized in 2012 after eleven years of drafting) • :scope proposed in CSS Selectors Level 4 in 2011 (still in draft) • Houdini specs proposed in 2016 (still active WIP)

Slide 30

Slide 30 text

EDVS Plugins

Slide 31

Slide 31 text

EDVS Plugins • Load stylesheets • Are aware of JavaScript logic and events • Populate virtual stylesheets • Interact with helper functions (mixins) to extend CSS

Slide 32

Slide 32 text

Template for a
 basic EDVS Plugin

Slide 33

Slide 33 text

function JSinCSS() { var tag = document.querySelector('#JSinCSS') if (!tag) { tag = document.createElement('style') tag.id = 'JSinCSS' document.head.appendChild(tag) } tag.innerHTML = ` body:before {
 content: '${innerWidth} x ${innerHeight}'; 
 } ` } window.addEventListener('load', JSinCSS) window.addEventListener('resize', JSinCSS) window.addEventListener('input', JSinCSS) window.addEventListener('click', JSinCSS)

Slide 34

Slide 34 text

What It's Doing • Find (or create and attach) a tag to populate • Hold CSS as a template string to be interpolated • Add event listeners to trigger stylesheet reprocessing

Slide 35

Slide 35 text

Average size of an EDVS plugin is ~100 SLOC

Slide 36

Slide 36 text

EDVS Mixins

Slide 37

Slide 37 text

EDVS Mixins • Can be used interchangeably with most EDVS plugins • Can search and modify the DOM • Return a string of CSS to the EDVS plugin • Borrow from Unix Philosophy (text-based, act as filter)

Slide 38

Slide 38 text

Template for a
 basic EDVS Mixin

Slide 39

Slide 39 text

function mixin(selector, rule) { var tag = document.querySelectorAll(selector) var style = '' var count = 0 for (var i=0; i

Slide 40

Slide 40 text

What It's Doing • Find all tags in document matching a selector • Assign a unique attribute to matching tags • Generate CSS targeting these unique attributes • Return CSS as a string • Extend by adding JS logic to decide which tags to style

Slide 41

Slide 41 text

Average size of an EDVS mixin is ~20 SLOC

Slide 42

Slide 42 text

It Doesn't Take Much Code • To conceive of new ways of thinking about design • To define a comfortable syntax for describing styles • To implement your desired workflow and tooling • To support a wide range of web browsers