Today I Learned

17 posts about #css

CSS - Element not perfectly centered

Sometimes you can notice that your element is not perfectly centered, doesn’t matter what centering method you use.

Most likely the reason is:

  • one element has even amount of pixels in size
  • second element has odd amount of pixels

Example:

  • Centering 40px element in 45px container
  • Centering 41px element in 46px container

Browser cannot split pixels in half!

image Link with examples

Setting half pixels won’t solve this issue

Half pixels only works for some browsers

Half pixels example

Safari half sticky element problem

Safari has weird bug about sticky elements in flexboxes, those sticky elements get scrolled after a while - more like in the half of the scrollbar, which is pretty confusing.

Problem:

Link

Open the link in the Safari, scroll the mid (yellow) section and notice that green bar (sticky element) gets also scrolled after a while.

Desktop elements

The problem is that in Safari sticky element cannot be flex item (direct child of a flex) whenever the sticky element is not the only child of a flex and the neighbour of sticky element has specified flex-shrink or flex-basis .

Solution:

Link

Simply add one additionalwrapper for sticky element and the problem should be solved :)

Great case for .webp format

While webp does not provide substantial size reduction when compared with JPG’s saved using MozJPEG encoder, there is a case where it really outperforms the competition and that is images with transparency.

For comparison we will use a photo of this fine gentleman, the base file is a full-color PNG that weighs 693kB. base

Normally to compress images with transparency, I used squoosh with OxiPNG, 256 colors and Dithering on, resulting in 214kB size. The image doesn’t look bad but we have lost some color, which is especially visible on the lips. png

With webp we set it to Browser WebP and quality 0,9. The result is only 63kB, with no colors lost and almost no artifacts. That is 30% of OxiPNG’s size! webp

Regarding the optimal compression setting, I would recommend 0,9 as the default base, with the max value of 0,99.

Box-shadow vs. drop-shadow from filters

When using box-shadow on an image with transparent background like png, the alpha channel gets neglected and the shadow is applied to the boundaries of the image, eg. box-shadow: 8px 16px 24px rgba(29,35,65,0.4). With CSS filters, like filter: drop-shadow(8px 16px 24px rgba(29,35,65,0.4)) the shadow is coming from the opaque sections of the image. You probably know that already.

shadows

Turns out the drop-shadow works the same way on background images. You can apply that effect either on the tag with the background image, or even the whole wrapper to get the same shadow from the text, inline svg icons, images and backgrounds.

backgrounds

Don't hide images with display: none

Images or wrapper elements that have display: none set, will still get loaded. So if you want to eg. load a different image for desktop and mobile, you can either:

  • use display: none, but with the loading="lazy" parameter set on img. Currently works on Firefox and Chromium
  • or use picture element with source. Over 95% support with a fallback to IE11 (just the img will be loaded.

See the example implementation of the 2 methods on Codepen.

Fix for margin hell that affects your app container

Your app container has:

max-height: 100vh;

But your page still have a scroll

Whenever you hover over <body> you will see that it’s slightly off the top of the page. Suprisingly it has also margin: 0; The problem is that the children of your app container have some margins and it’s too much work the get rid all of them.

To fix the issue add to your app container:

display: flow-root;

grid/table/flex also works but only the flow-root keeps the block behaviour.

NOTE It doesn’t work on IE and some other browsers you don’t care about :)

Test it out: Link

SVG icons in pseudo-elements

For adding icons into css pseudo-element you need to set some size and content with SVG. Ie:

.icon-one:after {
    content: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8zm3.9-11.7L10 14.2l-1.9-1.9c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4l2.6 2.6c.4.4 1 .4 1.4 0l6.6-6.6c.4-.4.4-1 0-1.4-.4-.4-1-.4-1.4 0z"/></svg>')

    display: inline-block;
    height: 20px;
    width: 20px;
}

But using this we’re not able to change the color of this SVG (icon). Property color or fill will not work.. :(

First idea

There is the second option of using SVG in pseudo-element, but this will not work on IE because it is not supporting mask-size property:

.icon-two:after {
    content: '';
    -webkit-mask: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8zm3.9-11.7L10 14.2l-1.9-1.9c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4l2.6 2.6c.4.4 1 .4 1.4 0l6.6-6.6c.4-.4.4-1 0-1.4-.4-.4-1-.4-1.4 0z"/></svg>') no-repeat 50% 50%;
    mask: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm0 18c-4.4 0-8-3.6-8-8s3.6-8 8-8 8 3.6 8 8-3.6 8-8 8zm3.9-11.7L10 14.2l-1.9-1.9c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4l2.6 2.6c.4.4 1 .4 1.4 0l6.6-6.6c.4-.4.4-1 0-1.4-.4-.4-1-.4-1.4 0z"/></svg>') no-repeat 50% 50%;
    -webkit-mask-size: cover;
    mask-size: cover;
    
    display: inline-block;
    height: 20px;
    width: 20px;
}

For changing color we’re using background-color property.

.icon-two:after {
    background-color: red;
}

Second idea

SASS - Comments indent matters

As you might know, SASS has pretty nice nesting feature:

.selector
  color: red
  font-size: 12px

and SASS allows you to put comments, but beware of your comment indent because:

This looks completely fine in this IDE right?!

Yeah… but how does compiler see that?

… Boom!

.dark-theme
 // variables 
  @import 'base/dark-variables'

 // fonts
  @import 'fonts/dark'

Sass stylelint

Adding stylelint on the project (using ie RM) in a few steps:

First, you need to install a package: yarn add stylelint.

Second, you need to install a package with config for stylelint. For SASS it is yarn add stylelint-config-sass-guidelines

Third, you need to add to the project file .stylelintrc.json with some config, ie:

{
  "extends": "stylelint-config-sass-guidelines",
  "rules": {
    "block-opening-brace-space-before": null,
    "color-hex-case": "upper",
    "declaration-block-trailing-semicolon": "never",
    "max-nesting-depth": 5,
    "number-leading-zero": null,
    "scss/percent-placeholder-pattern": ".",
    "scss/selector-no-redundant-nesting-selector": "allow",
    "selector-class-pattern": ".",
    "selector-no-qualifying-type": null,
    "selector-pseudo-element-colon-notation": "single"
  }
}

PS. For Scss I created config here.

The last step :) You need to run this on your IDE (or run it in the terminal by hand). Structure stylelint in RM #1

Gray gradients on Safari

On Safari browsers gradients that includes transparent color might seem to be more gray(ish) than on other devices:

Typical look: Typical look

IOS mobile: Gray Safari look The mid part seems to be gray instead of light red.

So you have styles as follows:

.a {
  background-image: linear-gradient(#f00, transparent);
}

.b {
  background-image: linear-gradient(#f00, rgba(0,0,0,0));
}

.c {
  background-image: linear-gradient(#f00, #00F0);
}

.d {
  background-image: linear-gradient(#f00, #f000);
}

Which result on desktop is: Desktop elements

But on IOS mobile devices: Safari elements

As you might have already noticed, IOS cuts out the alpha part from color and use it like a mid-gradient part.

In case A and B it would be #000 (black)

In case C it would be #00F (blue)

In case D it would be #F00 (red) which is a result we expected from the beginning.

Codepen if you want to test it on your own: Link

About using & in nested selectors with BEM

In normal SCSS/SASS file code:

.calendar-container--theme-third {
  .calendar-reservation {
    &__checkout-wrapper:not(&--modifier):before {
      content: 'abc';
    }
  }
}

will be parsed to:

.calendar-container--theme-third .calendar-reservation__checkout-wrapper:not(.calendar-container--theme-third .calendar-reservation--modifier):before {
  content: 'abc';
}

so when U need to use ie. not with ampersand U will get the whole parent selector

:not(.calendar-container--theme-third .calendar-reservation--modifier)

instead of the last parent in & place

:not(.calendar-reservation--modifier)

For this situation, I created plugin/mixin: BEM-parent-selector

This mixin gives you an option to add selector only for the last parent.

.calendar-container--theme-second-2 {
  .calendar-reservation {
    @include BEM-parent-selector('&__checkout-wrapper:not(&--modifier):before') {
      content: 'abc';
    }
  }
}

will be parsed to:

.calendar-container--theme-second-2 .calendar-reservation__checkout-wrapper:not(.calendar-reservation--modifier):before {
   content: 'abc';
 }

How to Share Variables Between Javascript and Scss/Sass

When you need to share variables between javascript and scss/ sass you can export variables in your style file:

$calendar-min-width: 80px;
$animation-length: 250ms;
$animation-length-ms: $animation-length + 10ms;

:export {
  calendarMinWidth: $calendar-min-width;
  animationMillis: $animation-length-ms;
}

and after that you’re able to get this in javascript:

const variables = {
  calendarMinHeight: parseInt(styles.calendarMinHeight),
  animationMillis: parseInt(styles.animationMillis),
};

A way to make autofilled inputs background transparent

Consider the following form

form

There’s an issue with that form - If it’s filled with chrome-stored credentials - it looks like this

filled_ugly_form

Here’s the selector for autofilled input: input:-webkit-autofill

It’s possible to change color of this box, like this:

input:-webkit-autofill,
input:-webkit-autofill:hover, 
input:-webkit-autofill:focus, 
input:-webkit-autofill:active  {
  -webkit-box-shadow: 0 0 0 30px red inset;
}

But this method is not useful, if you want to make the background transparent.

However there’s a way (or rather workaround) to make it transparent, like this:

input:-webkit-autofill,
input:-webkit-autofill:hover, 
input:-webkit-autofill:focus, 
input:-webkit-autofill:active  {
  transition: background-color 5000s;
  -webkit-text-fill-color: #fff !important;
}

This is basically equivalent to transparent background, unless you’ll wait really long for the animation to complete ;)

SASS / BEM - Not TIL but still some interesting magic

Case 1 - we don’t want to write parent classname again from deep nesting

.some-class
  $this: &

  &.--sub
    margin-top: 2.4rem
    #{$this}__title // so it is .some-class__title
      font-size: 1.7rem

equals:

.some-class.--sub
  margin-top: 2.4rem
.some-class.--sub .some-class__title
  font-size: 1.7rem

Case 2 - we want to have a (or any another tag) before parent class from deep nesting

.btn
  margin-top: 2.4rem
  @at-root a#{&} // so it is a.btn
    font-size: 1.7rem

equals:

.btn
  margin-top: 2.4rem
a.btn
  font-size: 1.7rem