Frontend Friedrich
Frontend Friedrich

Reputation: 151

Overwrite all given CSS styles with my CSS selector

I am writing an app for any CMS where I don't know the surrounding CSS or its selectors, but want my app to always look the same.

I'm having the issue, that if a external selector selects one of my elements for example via button{height: 100px;} and my selector button .class{width:100px;} does not specify the height attribute, the external selector puts this attribute onto my buttons and changes its style.

This is just a plain example and you might say, "just specify height in your selector, too", i cant put every thinkable attribute into my selectors and overwrite everything that could have somewhere been set.

See this fiddle for an easy example: https://jsfiddle.net/u4rvx6Lk/, where i don't want the first selector to set the border on my blue element, but of course, it does.

I have tried all: unset; and all: initial; but it just wont work and certainly wont in Internet Explorer.

I also know about scoped CSS but this also doesn't work very reliably.

(Further Info: It is an angular 1.x app that will mainly be injected into WordPress CMS pages, I compile my CSS using sass)

Upvotes: 2

Views: 380

Answers (4)

Michael Coker
Michael Coker

Reputation: 53709

See this fiddle for an easy example. Unfortunately, the first selector sets the border on my blue element.

The reason the border is being defined on the second box is because it is a button, which is where you defined the border. That's how CSS works. If you don't want the border on your second element, you need to either define border: 0; on the second element, or in the original selector defining the border, button, use something that excludes the second box, like button:not(#id1).

Upvotes: 0

Jim
Jim

Reputation: 4192

The solution that worked for me is using the CSS property all set to a value of revert.

.css-class {
    all: revert;
}

The mozilla docs for revert explains it nicely:

The revert CSS keyword rolls back the cascade so that the property takes on the value it would have had if there were no styles in the current style origin.

For firefox and chrome, use unset:

.css-class {
    all: unset;
}

See browser hacks for using "media query hacks" for checking the current browser for a cross-browser solution. Also, see this fiddle where the blue box does not inherit the height property:

I hope this solution will work in your project as well.

Upvotes: 2

lonesomeday
lonesomeday

Reputation: 238095

It is virtually impossible to stop CSS code from modifying your elements, as the web currently exists. What you are essentially talking about is sandboxing your elements, preventing them from being modified by other code.

You can do this with an iframe, but that's horrid and almost certainly too much faff.

On the other hand, the future is bright! There is a feature coming called the Shadow DOM, which essentially works to allow you to implement your own elements and sandbox them from the rest of your code. It's already how elements like input and video work behind the scenes: now we get to do what the browsers have been doing for years.

You can create an element: we'll call it solid-button.

<solid-button></solid-button>

We'll then write the code to go inside it. We'll put this in a template so it doesn't appear on the screen till we want it:

<template id="solid-button-src">
  <style>
    button {
      width: 100px;
      height: 100px;
      background-color: green;
      border: solid 1px black;
    }

  </style>
  <button></button>
</template>

We then use a little bit of Javascript to create the Shadow DOM and add the content in the template into it:

customElements.define('solid-button', class extends HTMLElement {
  constructor() {
    super();

    const shadowRoot = this.attachShadow({
      mode: 'open'
    });
    const template = document.querySelector('#solid-button-src');
    const clone = document.importNode(template.content, true);
    shadowRoot.appendChild(clone);
  }
});

This works in some browsers now, but nowhere near enough to make it production-ready. Your user can apply styles to solid-button, but they won't affect the content inside. If you have an up-to-date browser, you can see this in effect below.

Obviously this is not a solution for you now, but it might be the future.

customElements.define('solid-button', class extends HTMLElement {
  constructor() {
    super();

    const shadowRoot = this.attachShadow({
      mode: 'open'
    });
    const template = document.querySelector('template');
    const clone = document.importNode(template.content, true);
    shadowRoot.appendChild(clone);
  }
});
button {
  height: 100px;
  width: 100px;
  background-color: red;
  border: solid 1px black;
}

.class1 {
  height: 100px;
  width: 100px;
  background-color: blue;
}

my-button {
  background: red; /* does nothing visible */
}
<button></button>
<button id="id1" class="class1"></button>
<solid-button></solid-button>
<solid-button class="class1"></solid-button>

<template>
  <style>
    button {
      width: 100px;
      height: 100px;
      background-color: green;
      border: solid 1px black;
    }

  </style>
  <button>
    <slot name="content"></slot>
  </button>
</template>

Upvotes: 1

Frontend Friedrich
Frontend Friedrich

Reputation: 151

So I somehow made it work. Thanks for the answers.

all: unset and revert didnt work for me, all: initial did.

Furthermore i will give my internal classes very specific names. Like class=my-project-classto prevent external class-selectors from injecting their own attributes.

Also this should not work in IE.

See this Fiddle: https://jsfiddle.net/Lvxxnh8r/13/

Upvotes: 0

Related Questions