The Edge of CSS

D2ddf71464b997e71ddb17c1812285b9?s=47 Tommy Hodgins
April 20, 2017

The Edge of CSS

Have you ever felt limited by CSS and found yourself needing to apply styles to an element CSS can't quite select, or based on a breakpoint CSS isn't quite aware of? Follow along as Tommy shares a CSS extension that adds a few new abilities to how you can write CSS, and learn how you can begin using concepts like style scoping, element and container queries, and much more in your own projects right away!

D2ddf71464b997e71ddb17c1812285b9?s=128

Tommy Hodgins

April 20, 2017
Tweet

Transcript

  1. The Edge of CSS by Tommy Hodgins

  2. Have you ever found yourself at the edge of what

    CSS can do?
  3. Signs you’re on the edge • using polyfills for unsupported

    features
  4. Signs you’re on the edge • using polyfills for unsupported

    features • generating unique IDs or classes for elements
  5. Signs you’re on the edge • using polyfills for unsupported

    features • generating unique IDs or classes for elements • duplicating code only to change breakpoints
  6. Signs you’re on the edge • using polyfills for unsupported

    features • generating unique IDs or classes for elements • duplicating code only to change breakpoints • so many more (see Sisyphus)
  7. In situations where your only option is writing custom JavaScript

    to apply a style to an element…
  8. Think how CSS could be extended to reach that situation

    instead
  9. What if you could extend CSS in the browser to

    add new features and functionality?
  10. What if instead of a CSS preprocessor you had a

    CSS reprocessor?
  11. What is a CSS reprocessor?

  12. A CSS reprocessor is a tool to recalculate your styles

    as often as needed after the page has loaded, and after user interaction
  13. Thinking beyond the edge of CSS

  14. My journey beyond the edge began in 2014 with the

    EQCSS project
  15. The goal of EQCSS was to add the features I

    wanted to use to CSS so I could use them
  16. The hard question:

  17. What’s the smallest set of ideas that could be added

    to CSS to lead to the functionality I wanted?
  18. Our 4 What-ifs

  19. What if CSS Could • Set a scope for a

    selector like a @media query but for individual elements
  20. What if CSS Could • Set a scope for a

    selector like a @media query but for individual elements • Add a block of CSS styles to the page when that selector is true
  21. What if CSS Could • Set a scope for a

    selector like a @media query but for individual elements • Add a block of CSS styles to the page when that selector is true • Add responsive conditions to that selector
  22. What if CSS Could • Set a scope for a

    selector like a @media query but for individual elements • Add a block of CSS styles to the page when that selector is true • Add responsive conditions to that selector • Evaluate JavaScript from the context of the element you are styling and use those values in the CSS you're applying to the page
  23. With those four ideas added on top of what CSS

    already does many new responsive techniques become very simple to express
  24. The amount of JavaScript required to add support for these

    four ideas is about 3000 lines of code, or about 3kb when minified and gzipped
  25. But there’s no end to the amount of new responsive

    techniques possible…
  26. New techniques possible • scoped styles • element queries &

    container queries • breakpoints based on element properties • variables scoped to individual elements • self-responsive HTML components • parent selector, previous element selector, etc
  27. • element-based units • scroll-based responsive styles • smarter attribute

    selectors (number comparison) • make any element scalable • work easily with aspect ratios • polyfill and emulate unsupported CSS features • many more… (search ‘EQCSS’ on CodePen for 100+)
  28. EQCSS has been used in production for 2 years

  29. EQCSS allows us to • Build layout-independent design components

  30. EQCSS allows us to • Build layout-independent design components •

    Scope, isolate, and transplant existing layout parts from one project to another
  31. EQCSS allows us to • Build layout-independent design components •

    Scope, isolate, and transplant existing layout parts from one project to another • Support modern responsive layouts in older browsers
  32. EQCSS allows us to • Build layout-independent design components •

    Scope, isolate, and transplant existing layout parts from one project to another • Support modern responsive layouts in older browsers • Keep our CSS short and our workflow simple
  33. Moving beyond The edge of CSS

  34. How EQCSS works • If any @element queries are found,

    the selector(s), condition(s), and styles are extracted
  35. How EQCSS works • EQCSS recalculates on: DOM ready, scroll,

    resize, input, click, mousedown, mousemove, mouseup
  36. How EQCSS works • Any time there is at least

    1 element in the DOM matching a selector in the selector list of an @element query • And all conditions are true (if any included) • Add the block of styles to the page
  37. How EQCSS works • And while we’re applying CSS to

    the page: provide a way to evaluate JavaScript from the context of each element we’re styling
  38. With @media queries the most common responsive features are min/max

    width and height, but with @element queries, many new responsive features make sense
  39. Responsive conditions • width • height • characters (number of

    characters of text content) • lines (number of lines of text) • children (number child elements) • scroll-x (horizontal scroll position) • scroll-y (vertical scroll position) • aspect-ratio • orientation
  40. Inside the scope of the @element query, new selectors are

    possible
  41. Meta-Selectors • $this is the scoped element • $parent is

    the parent of the scoped element • $prev is the element before the scoped element • $next is the element after the scoped element
  42. Evaluating the properties of the elements as we style them

    adds the possibility for new units
  43. Similar to how viewport units (VW, VH, VMIN and VMAX)

    refer to viewport-based percentages, what if element-based units referred to dimensions of individual elements
  44. Element-based units • EW is 1% element width • EH

    is 1% element height • EMIN is 1% element minimum length • EMAX is 1% element maximum length
  45. Eval • eval('') uses the output of JavaScript evaluated from

    the context of each element the query applies to anywhere inside the styles being added to the page
  46. Some CSS+ Techniques

  47. Imagine if CSS had more dynamic values like currentColor

  48. What if you could use values like currentWidth or parentChildren

    in your CSS?
  49. Many values like this, while out of reach of CSS,

    are simple to measure with JavaScript if you could evaluate from the context of each element you are styling
  50. Imagine if an iframe had a way to scale height

    responsively based on its own aspect ratio
  51. iframe { width: 100%; height: calc(currentWidth / (16/9)); }

  52. currentWidth is kind of like 100ew

  53. @element iframe { $this { width: 100%; height: calc(100ew /

    (16/9)); } }
  54. What if CSS could access each iframe’s own width and

    height attributes, something kind of like attr(width) or attr(height)
  55. @element iframe { $this { width: 100%; height: calc(100ew /

    (attr(width)/attr(height))); } }
  56. We can access these values for $this element by using

    eval('width') and eval('height')
  57. @element iframe { $this { width: 100%; height: calc(100ew /

    (eval('width')/eval('height'))); } }
  58. Here eval('width') is giving us the same result in our

    CSS as el.width or el.getAttribute('width') would return in JavaScript
  59. No matter what the current width, we can always scale

    the height of each iframe based on its own aspect ratio, determined by its own width and height attributes
  60. What about a parent selector, or :has()?

  61. Have you ever wanted to apply a style to an

    element that contains another element?
  62. Suppose we want to make an h2 red, but only

    if it contains a strong element inside
  63. This is possible with CSS using h2:has(strong), but unfortunately no

    web browsers support :has()
  64. h2:has(strong) { color: red; }

  65. This is equivalent to styling $this inside an element query

    for h2:has(strong) if it had support…
  66. @element h2:has(strong) { $this { color: red; } }

  67. Since we can’t use :has(), could we use querySelector() inside

    eval('') to check for strong tags?
  68. @element h2 { eval('querySelector("strong") && "$this"') { color: red; }

    }
  69. Inside our element query for h2 tags, if querySelector('strong') returns

    true, the rule applies to $this
  70. …Or maybe you could apply a style to the parent

    of every strong tag if the $parent is also an h2
  71. @element strong { h2$parent { color: red; } }

  72. Or another way to do the same thing would be

    to apply a style to the $parent of any h2 strong
  73. @element h2 strong { $parent { color: red; } }

  74. Comparing attribute values as numbers

  75. CSS has an attribute selector that can select elements based

    on comparing values as strings
  76. But what if you could compare attributes as numbers, like

    [value<0] or [value >=50]
  77. What if you could use a selector like [type=number][value<0] to

    select inputs with values less than 0
  78. [type=number][value < 0] { border: 1px solid red; }

  79. We can use [value=0] and match the number 0 as

    a string, but not compare as a number with >, <, >=, or <=
  80. @element [type=number] { eval('(value < 0) && "$this"') { border:

    1px solid red; } }
  81. Here we add a red border to every [type=number] element

    with a value < 0
  82. Can we create automatic hyphenation based on an element's own

    width?
  83. Imagine you want to set hyphens for heading elements when

    they are less than 35em wide, you may use a @media query like this
  84. @media (max-width: 35em) { h1, h2, h3, h4, h5, h6

    { hyphens: auto; } }
  85. We can use an @element query to make the breakpoint

    relative to the width of each heading tag rather than the width of the browser’s viewport
  86. @element h1, h2, h3, h4, h5, h6 and (max-width: 35em)

    { $this { hyphens: auto; } }
  87. Now our heading tags will have hyphenation when any of

    them are less than 35em wide, regardless of the width of the browser
  88. Think of how you might accomplish some of the the

    following ideas…
  89. • change the background-position of a background image based on

    an element’s aspect-ratio
  90. • change the background-position of a background image based on

    an element’s aspect-ratio • style an input that's empty (input:empty doesn't work in CSS)
  91. • change the background-position of a background image based on

    an element’s aspect-ratio • style an input that's empty (input:empty doesn't work in CSS) • create scalable/responsive CSS border art
  92. • change the background-position of a background image based on

    an element’s aspect-ratio • style an input that's empty
 (input:empty doesn't work in CSS) • create scalable/responsive CSS border art • style a textarea or input so it expands to fit as many characters or lines of text as are inside
  93. To get started, add the following line of code to

    your project:
  94. <script src= //eqcss.com/EQCSS.js> </script>

  95. Reprocessing CSS

  96. If you are going to try a CSS reprocessor, here

    are things to keep in mind
  97. Which events should trigger recalculations? • things like: load, resize,

    input and keyboard, clicks and touch events, scroll on elements, etc. • can you only listen to events that make sense to the styles you are recalculating? • can you use resize observers for width/height? • do mutation observers help?
  98. Is your reprocessor integrated inside a specific tech stack or

    tool, or does it exist as a plugin that runs in the browser like a shim or polyfill?
  99. What’s your exit strategy or migration path for porting code

    away from the reprocessor if you need to at some point in the future?
  100. THANKS! Visit elementqueries.com for demos, docs, & links