Today I Learned

29 posts about #css

Pass hex color to fill svg in background url (SCSS)

Passing svg into background url might be useful for eg. in case of dropdown arrow selects.

but there is a problem if you want to pass dynamically color to fill that svg. url is not accepting # from that hex.

I found that if we use

@use "sass:string";
@use "sass:color";

we can build something similar to encodeURI

@function escape-url-hex-color($hex_color) {
  $ie-color-string: color.ie-hex-str($hex_color);
  $string: string.quote($ie-color-string);
  $color: string.slice($string, 4);
  @return '%23' + $color;
}

then we can easily use it to set color variable and pass it to fill attribute.

Make sure borders are not doubled in list

When I had a static table (made with divs, because every field was a separate page on mobile), borders were no problem. However, our’s client designer decided it would be nice to indicate required fields with red border and pink background. It caused me some trouble due to doubled borders.

Screenshot-2021-09-08-at-10-15-01

My field component looks like that Screenshot-2021-09-08-at-14-05-38

So I could use adjacent sibling selector to check whether before field I had a field with error. If it’s true, then I hide top border since it’s already present for the required field. Screenshot-2021-09-09-at-08-37-36

Now it works!

Screenshot-2021-09-08-at-10-15-13

Disappearing gradient from SVG

If we have two (or more) instances of an svg icon with a gradient and we hide the first one, the gradients in other icons disappear. This happens only if we hide it with display: none , because it cannot find the source of the gradient (eg. fill = “url (‘#gradient1’)”). It does not matter if the gradient is defined again in the second svg, always the first id is the source. Podziękowania dla Janka!

SVG-disappear

Enable ellipsis only on the larger sibling

We have an element that has a max-width, and contains 2 child elements with text. If the text gets too long, we use ellipsis on them. But there are a few cases to cover:

  • if one of the elements is smaller than 50%, we only truncate the bigger one (it can be either of these elements),
  • we truncate both equally if they exceed 50%,
  • and of course no truncation if both are smaller.

At first I thought it’s impossible with CSS, as in flex the truncation occurred on both elements and table had problems with width, but grid worked just fine, as you can see in the codepen here.

Be careful about CSS shorthand properties

It’s always better to set certain property instead of using shorthand unless you exactly know what you’re doing.

Example:

  /*doesn't work*/
  background-repeat: no-repeat
  background-position: center
  background: url('http://someurl.jpg')


  /* works*/
  background-repeat: no-repeat
  background-position: center
  background-image: url('http://someurl.jpg')

  /* also works*/
  background: url('http://someurl.jpg')
  background-repeat: no-repeat
  background-position: center

Why?

background-image sets only image as a background

background property on the other hand sets all of these things:

  • background-attachment
  • background-clip
  • background-color
  • background-image
  • background-origin
  • background-position
  • background-repeat
  • background-size

If you don’t specify them intentionally those would be set to their default or unset value

Codepen: link

This is the same case as:

/* won't work - results in right margin to be 8px */
margin-right: 16px
margin: 8px

/* works */
margin: 8px
margin-right: 16px

What other shorthand properties should you be also careful about?

Those:

image

MDN source

How to style broken images in CSS (and a bit of JS)

When working on a client’s website, I’ve spotted some images looking like that:

base

Turns out there are ways to change how the broken images look, but it’s a bit different for every browser, you can apply styles like background-color to:

  • the image itself,
  • the ::before element of the image,
  • using special selectors for not working images,
  • or you need to use the onerror property (or some other form of detecting a broken image in JS).

Click to see examples of how to deal with broken images, and run the examples in Chrome/Firefox/Safari to see the differences.

Omit additional wrappers in CSS, display: contents;

In situation where you want to style elements like those were neighbours, but unfortunately two of them are in additional wrapper that you can’t get rid of:

<div class="flex">
  <div class="red-block"></div>
  <div class="wrapper">
    <div class="el">1</div>
    <div class="el">2</div>
  </div>
</div>

and you want them to be inside flex and be placed by eg. space-between and this is the result:

image

Add display: contents; to the wrapper and problem is solved: image

display: contents makes the element to ignore itself in styling.

Browser support: image

Codepen: here

CSS user preferences media queries

We can aim user preferences by specific media queries which allows for example to read system color scheme:

@media (prefers-color-scheme: dark) {
   /* some styling */
}

Or also we can read if user prefers reduced motion

@media (prefers-reduced-motion) {
  /* ... */
}

Those two already have big browser support, in near future we can expect also: prefers-reduced-transparency, prefers-contrast and prefers-reduced-data

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