CS Chris Smith
CodePen Mastodon X (formerly Twitter) Feed

Setting Default Styling for Custom UI Components

by Chris Smith

I thought I'd share a challenge I'm facing at the moment. A lot of modern JavaScript frameworks like React and Angular encourage building reusable UI components. Using JavaScript and HTML templates seems like an excellent approach to development on larger sites but styling these components can bring a few issues.

Browsers can handle styling standard HTML web components, like <select> or <button>. They may vary in how they display these but they all have a default way which can then be overwritten with CSS. This is stored as a kind of internal CSS class in the browser and is easy to overwrite as it has almost zero specificity. Adding an element selector 'select', a class '.picker' or an id '#picker' will easily take control over the styling.

The problem arises when we create custom components that don't have any default, zero specificity styling. Any styling that we do add when we design components has specificity so we immediately have a fight on our hands for which styling declaration gets control. We need some level of default styling, usually for layout as much as anything, to ensure that the browser cannot just show a mess of HTML elements.

Here are a few examples of how default component styling could be set and the problems associated with each approach.

  1. Setting the CSS in an external stylesheet

    We set the styling in its own CSS file. I'd say that this is the approach used by most UI tools like Bootstrap or jQuery UI. Various HTML elements in the component are given classes, which can be overwritten.

    The problem is that they can only be overwritten by using higher specificity or by making sure that the overwriting style comes after the external CSS in the document's source order. It also means an additional HTTP request to load a usually small amount of styling data.

  2. Setting the style in a style attribute

    We could add style attributes directly into the HTML tags. The problem here is obvious - super high specificity. This makes the styling very difficult to overwrite. You're pretty much forced into using '!important' declarations in your CSS, which not only feels wrong but really leaves you with nowhere to go.

    It goes against the separation of concerns - content and styling but as this is intended to be default styling, which we would hope to overwrite, I'm not sure this matters.

  3. Setting the style in a HTML <style> tag

    We could place a block of HTML with <style> tags directly before or after the component HTML so that it effectively ships together. Placing <style> tags in the <body> was invalid in HTML4 but, if I've understood it correctly, can be used in HTML5 by using the scoped attribute, e.g. <style type="text/css" scoped></style>. Browser support for this to be properly valid is not great at the time of writing (early 2016) - we get warning messages in consoles or code editors - but it seems to display as intended.

    The problem with this approach is that the style is declared in the <body> whereas the overwriting CSS is probably declared in the <head> and so will not overwrite it without upping the specificity.

    W3C Recommendation - Stylesheets W3C Recommendation - Style Scoped

  4. Setting the style with JavaScript

    The final option is to set styling using JavaScript, which effectively just inserts style attributes into the component's HTML elements like in approach 2.
I'm wondering if there may be a solution where we have a default styles object which defines style properties in our existing component JavaScript file. We then check to see what styling is applied using the browser's getComputedStyle() function. For each of the properties we check if a rule is already set, presumably by CSS that is in the <head>, and if not we apply the style from the object. It's almost like required validation for styling. I don't imagine it's going to be very efficient but it may be a way of ensuring a minimum level of styling without restricting additional styling via CSS. Assuming that our script file is at the end of the <body>, by using only the existing resources we could save an additional HTTP request.