stylis icon indicating copy to clipboard operation
stylis copied to clipboard

Remove outdated prefixes?

Open oliviertassinari opened this issue 2 years ago • 5 comments

Stylis includes a prefixer module:

import { prefixer } from 'stylis';

This is very helpful as there are CSS properties that need vendor prefixes. For example, user-select: none; still requires it in browsers that have an important global usage rate.

However, I have noticed a couple of prefixes that I would argue are more harmful than beneficial. Mainly, the flexbox properties. The global need for the prefix seems to be 0.48% (0.01 + 0.14 + 0.05 + 0.01 + 0.02 + 0.03 + 0.01 + 0.1 + 0.11). Do we need to keep it, at this point?

By removing it, we would gain:

  1. A better DX when debugging CSS. When this module is used with emotion, you get this kind of output by default

Before

Screenshot 2022-01-22 at 19 31 18

After

Screenshot 2022-01-22 at 19 31 30
  1. A smaller bundle.

Actually, this might simply be the root cause of a different problem: What's the browser support target? Do we have something like this https://github.com/kripod/style-vendorizer#browser-support?


  • A fun read https://css-tricks.com/is-vendor-prefixing-dead/#aa-prefixing-in-2021
  • A related thread https://github.com/styled-components/styled-components/issues/285

oliviertassinari avatar Jan 22 '22 18:01 oliviertassinari

Moving this comment here where we can track the discussion.

In a way, this could be seen as a breaking change.

In a way you could avoid a breaking change by exporting a prefixer, that is a 1-1 clone of the current prefixer with the suggested amendments, i.e export prefixer_bleeding_age™ given that the current prefixer is not privileged and is as much a middleware as any other.

thysultan avatar Jan 22 '22 19:01 thysultan

Yep, it's only that with a different prefixer one would always bundle both as the prefixer is included in the default plugins. We've included that to make upgrading to Emotion 11 easier - but this is likely to change in Emotion 12 (no plans for it right now though)

cc @mitchellhamilton

Andarist avatar Jan 22 '22 20:01 Andarist

I'd also love to know how to disable vendor prefixing. We don't need any of it in our environments, so it's injecting a lot of un-needed CSS.

matthew-dean avatar May 13 '22 20:05 matthew-dean

If you are using Emotion then u can use CacheProvider and skip providing the prefixer plugin, u can roughly see how it could be done here: https://emotion.sh/docs/cache-provider

Andarist avatar May 13 '22 21:05 Andarist

For those who would like to remove MS prefix

import {
  charat,
  combine,
  copy,
  DECLARATION,
  hash,
  indexof,
  KEYFRAMES,
  match,
  MOZ,
  replace,
  RULESET,
  serialize,
  strlen,
  WEBKIT
} from 'stylis'

/**
 * @param {string} value
 * @param {number} length
 * @param {object[]} children
 * @return {string}
 */
function prefix(value, length, children) {
  switch (hash(value, length)) {
    // color-adjust
    case 5103:
      return WEBKIT + 'print-' + value + value
    // animation, animation-(delay|direction|duration|fill-mode|iteration-count|name|play-state|timing-function)
    case 5737:
    case 4201:
    case 3177:
    case 3433:
    case 1641:
    case 4457:
    case 2921:
    // text-decoration, filter, clip-path, backface-visibility, column, box-decoration-break
    case 5572:
    case 6356:
    case 5844:
    case 3191:
    case 6645:
    case 3005:
    // mask, mask-image, mask-(mode|clip|size), mask-(repeat|origin), mask-position, mask-composite,
    case 6391:
    case 5879:
    case 5623:
    case 6135:
    case 4599:
    case 4855:
    // background-clip, columns, column-(count|fill|gap|rule|rule-color|rule-style|rule-width|span|width)
    case 4215:
    case 6389:
    case 5109:
    case 5365:
    case 5621:
    case 3829:
      return WEBKIT + value + value
    // tab-size
    case 4789:
      return MOZ + value + value
    // appearance, user-select, transform, hyphens, text-size-adjust
    case 5349:
    case 4246:
    case 4810:
    case 6968:
    case 2756:
      return WEBKIT + value + MOZ + value + value
    // writing-mode
    case 5936:
      switch (charat(value, length + 11)) {
        // vertical-l(r)
        case 114:
          return WEBKIT + value + value
        // vertical-r(l)
        case 108:
          return WEBKIT + value + value
        // horizontal(-)tb
        case 45:
          return WEBKIT + value + value
        // default: fallthrough to below
      }

    // flex, flex-direction, scroll-snap-type, writing-mode
    case 6828:
    case 4268:
    case 2903:
      return WEBKIT + value + value
    // order
    case 6165:
      return WEBKIT + value + value
    // align-items
    case 5187:
      return (
        WEBKIT +
        value +
        replace(value, /(\w+).+(:[^]+)/, WEBKIT + 'box-$1$2') +
        value
      )
    // align-self
    case 5443:
      return WEBKIT + value + value
    // align-content
    case 4675:
      return WEBKIT + value + value
    // flex-shrink
    case 5548:
      return WEBKIT + value + value
    // flex-basis
    case 5292:
      return WEBKIT + value + value
    // flex-grow
    case 6060:
      return (
        WEBKIT + 'box-' + replace(value, '-grow', '') + WEBKIT + value + value
      )
    // transition
    case 4554:
      return (
        WEBKIT +
        replace(value, /([^-])(transform)/g, '$1' + WEBKIT + '$2') +
        value
      )
    // cursor
    case 6187:
      return (
        replace(
          replace(
            replace(value, /(zoom-|grab)/, WEBKIT + '$1'),
            /(image-set)/,
            WEBKIT + '$1'
          ),
          value,
          ''
        ) + value
      )
    // background, background-image
    case 5495:
    case 3959:
      return replace(value, /(image-set\([^]*)/, WEBKIT + '$1' + '$`$1')
    // justify-content
    case 4968:
      return (
        replace(
          replace(value, /(.+:)(flex-)?(.*)/, WEBKIT + 'box-pack:$3'),
          /s.+-b[^;]+/,
          'justify'
        ) +
        WEBKIT +
        value +
        value
      )
    // justify-self
    case 4200:
      if (!match(value, /flex-|baseline/)) return value

      break
    // grid-template-(columns|rows)
    case 2592:
    case 3360:
      return value
    // grid-(row|column)-start
    case 4384:
    case 3616:
      if (
        children &&
        children.some(function (element, index) {
          return (length = index), match(element.props, /grid-\w+-end/)
        })
      ) {
        return value
      }

      return value
    // grid-(row|column)-end
    case 4896:
    case 4128:
      return value
    // (margin|padding)-inline-(start|end)
    case 4095:
    case 3583:
    case 4068:
    case 2532:
      return replace(value, /(.+)-inline(.+)/, WEBKIT + '$1$2') + value
    // (min|max)?(width|height|inline-size|block-size)
    case 8116:
    case 7059:
    case 5753:
    case 5535:
    case 5445:
    case 5701:
    case 4933:
    case 4677:
    case 5533:
    case 5789:
    case 5021:
    case 4765:
      // stretch, max-content, min-content, fill-available
      if (strlen(value) - 1 - length > 6)
        switch (charat(value, length + 1)) {
          // (m)ax-content, (m)in-content
          case 109:
            // -
            if (charat(value, length + 4) !== 45) break
          // (f)ill-available, (f)it-content
          case 102:
            return (
              replace(
                value,
                /(.+:)(.+)-([^]+)/,
                '$1' +
                  WEBKIT +
                  '$2-$3' +
                  '$1' +
                  MOZ +
                  (charat(value, length + 3) == 108 ? '$3' : '$2-$3')
              ) + value
            )
          // (s)tretch
          case 115:
            return ~indexof(value, 'stretch')
              ? prefix(
                  replace(value, 'stretch', 'fill-available'),
                  length,
                  children
                ) + value
              : value
        }

      break
    // grid-(column|row)
    case 5152:
    case 5920:
      return replace(
        value,
        /(.+?):(\d+)(\s*\/\s*(span)?\s*(\d+))?(.*)/,
        function () {
          return value
        }
      )
    // position: sticky
    case 4949:
      // stick(y)?
      if (charat(value, length + 6) === 121)
        return replace(value, ':', ':' + WEBKIT) + value

      break
    // display: (flex|inline-flex|grid|inline-grid)
    case 6444:
      switch (charat(value, charat(value, 14) === 45 ? 18 : 11)) {
        // (inline-)?fle(x)
        case 120:
          return (
            replace(
              value,
              /(.+:)([^;\s!]+)(;|(\s+)?!.+)?/,
              '$1' +
                WEBKIT +
                (charat(value, 14) === 45 ? 'inline-' : '') +
                'box$3' +
                '$1' +
                WEBKIT +
                '$2$3'
            ) + value
          )
        // (inline-)?gri(d)
        case 100:
          return value
      }

      break
    // scroll-margin, scroll-margin-(top|right|bottom|left)
    case 5719:
    case 2647:
    case 2135:
    case 3927:
    case 2391:
      return replace(value, 'scroll-', 'scroll-snap-') + value
  }

  return value
}

function stylisCustomPrefixer(element, index, children, callback) {
  if (element.length > -1)
    if (!element.return)
      switch (element.type) {
        case DECLARATION:
          element.return = prefix(element.value, element.length, children)

          return
        case KEYFRAMES:
          return serialize(
            [
              copy(element, {
                value: replace(element.value, '@', '@' + WEBKIT)
              })
            ],
            callback
          )
        case RULESET:
          if (element.length)
            return combine(element.props, function (value) {
              switch (match(value, /(::plac\w+|:read-\w+)/)) {
                // :read-(only|write)
                case ':read-only':
                case ':read-write':
                  return serialize(
                    [
                      copy(element, {
                        props: [replace(value, /:(read-\w+)/, ':' + MOZ + '$1')]
                      })
                    ],
                    callback
                  )
                // :placeholder
                case '::placeholder':
                  return serialize(
                    [
                      copy(element, {
                        props: [
                          replace(
                            value,
                            /:(plac\w+)/,
                            ':' + WEBKIT + 'input-$1'
                          )
                        ]
                      }),
                      copy(element, {
                        props: [replace(value, /:(plac\w+)/, ':' + MOZ + '$1')]
                      })
                    ],
                    callback
                  )
              }

              return ''
            })
      }
}

module.exports = stylisCustomPrefixer

kc980602 avatar Aug 12 '22 10:08 kc980602