2

I have a custom web-component which is basically an SVG-Icon:

<custom-icon>
    <svg>{svg-stuff}</svg>
</custom-icon>

I want to be able to change it's size by applying CSS like so:

custom-icon {
    width: 20px;
}

But I also would like to have a fallback default value when no CSS is applied. However, when I inline some CSS like <custom-icon style="width:15px"> it just overwrites all CSS I apply afterwards. How can I have the default "15px" only apply if there is no custom CSS?

MWE:

class CustomIcon extends HTMLElement {
  constructor() {
    super();
    
    let size = "100px"
    
    this.style.height = size;
    this.style.width = size;
    
    this.style.background = "firebrick"
    this.style.display = "block"
  }
}

window.customElements.define('custom-icon', CustomIcon);
custom-icon {
  --icon-size: 50px;
  height: var(--icon-size);
  width: var(--icon-size);
}
<custom-icon />
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
leonheess
  • 16,068
  • 14
  • 77
  • 112
  • 1
    Add a class to your component like this ... and change CSS tag declaration to class one: custom-icon { => .custom-class { width: 15px; } – Maksim Tikhonov Jan 16 '20 at 09:42
  • `have a fallback default value when no CSS is applied` what do you mean by that? why should the css not be applied? – Thomas Jan 16 '20 at 09:42
  • 1
    if you have an inline style your css must use `!important` - it's the only way to override inline styles - but be careful when using it as it can become a specificity nightmare – Pete Jan 16 '20 at 09:43
  • @MaksimTikhonov Could you modify the snippet accordingly? I'm not sure what you mean – leonheess Jan 16 '20 at 09:49
  • @Pete I'd like to avoid that if possible – leonheess Jan 16 '20 at 09:49
  • @Thomas Imagine this icon-element being used in many instances. Some might want it in a specific size and will declare this in the CSS. Most, however, will use the default size and will therefore not apply any CSS. – leonheess Jan 16 '20 at 09:51
  • 1
    Then I guess the way to do it would be to have your bare selector as your default value, then add a class for your overrides – Pete Jan 16 '20 at 09:52
  • @Pete, could you elaborate in an answer? – leonheess Jan 16 '20 at 09:52

3 Answers3

2

If the content of your custom element is encapsulated in a Shadow DOM, which is a recommended practice, you can use the :host pseudo-class to define a default style.

Then if you define a global style for your custom element it will override the one defined with :host.

customElements.define( 'custom-icon', class extends HTMLElement {
  constructor() {
    super()
    let size = 100
    this.attachShadow( { mode: 'open' } )
        .innerHTML = `
          <style>
            :host {
               display: inline-block ;
               height: ${size}px ;
               width: ${size}px ;
               background-color: firebrick ; 
               color: white;
            }
          </style>
          <slot></slot>`
  }
} )
custom-icon#i1 {
  --icon-size: 50px;
  height: var(--icon-size);
  width: var(--icon-size);
}
<custom-icon id="i1">sized</custom-icon>
<hr>
<custom-icon>default</custom-icon>
Supersharp
  • 29,002
  • 9
  • 92
  • 134
1

The order is applied according to the cascade.

CSS applied via the style attribute is at the bottom of the cascade. In effect, if you don't specify via the attribute when it falls back to the stylesheet.

So 20px is the fallback for when you don't specify 15px.


You could write your fallback CSS using another rule-set with a less specific selector (although the only thing less specific than a single type selector (like custom-icon) is the universal selector (*) which isn't helpful) so you would need to replace custom-icon with something more specific.

The other option is the take the sledgehammer approach and make every rule in your ruleset !important.


The best option would probably be to fix whatever circumstance might cause your CSS to be missing in the first place.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
0

You can consider data attribute and then use that attribute as a fallback for the custom property.

You can see in the below, that the size will have no effect until we remove the custom property (by setting initial)

class CustomIcon extends HTMLElement {
  constructor() {
    super();
    
    this.style.height = `var(--icon-size, ${this.getAttribute('size')})`;
    this.style.width = `var(--icon-size, ${this.getAttribute('size')})`;
    
    this.style.background = "firebrick"
    this.style.display = "block"
  }
}

window.customElements.define('custom-icon', CustomIcon);
custom-icon {
  --icon-size: 50px;
  margin:5px;
}
<custom-icon size="15px"></custom-icon>
<custom-icon size="25px"></custom-icon>
<custom-icon size="2050px"></custom-icon>


<custom-icon size="200px" style="--icon-size:initial"></custom-icon>

Related question to understand the use of initial : CSS custom properties (variables) for box model

Another example where the custom property is not set initially.

class CustomIcon extends HTMLElement {
  constructor() {
    super();

    this.style.height = `var(--icon-size, ${this.getAttribute('size')})`;
    this.style.width = `var(--icon-size, ${this.getAttribute('size')})`;

    this.style.background = "firebrick"
    this.style.display = "block"
  }
}

window.customElements.define('custom-icon', CustomIcon);
custom-icon {
  margin: 5px;
}

.set {
  --icon-size: 50px;
}
<div class="set">
  <custom-icon size="15px"></custom-icon>
  <custom-icon size="25px"></custom-icon>
  <custom-icon size="2050px"></custom-icon>
</div>

<custom-icon size="200px" ></custom-icon>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 1
    @leonheess here is some related Answers to get more *powerful* things around CSS variables and their fallback: https://stackoverflow.com/q/53239880/8620333 / https://stackoverflow.com/a/49618941/8620333 – Temani Afif Jan 16 '20 at 10:32
  • 1
    In custom elements, *read access* on attributes **is not allowed in the `constructor`**. It disallows for dynamical creation of the element using `new CustomIcon` or `document.createElement('custom-icon')`. Also you call them *data attribute* - that term is reserved for `data-` prefixed attributes. – connexo Jan 17 '20 at 08:52
  • 1
    https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance **Requirements for custom element constructors and reactions** [...] *The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.* – connexo Jan 17 '20 at 08:58
  • @connexo What do you recommend instead? – leonheess Jan 17 '20 at 12:30