Skip to content

Writing styles

tophf edited this page Nov 28, 2023 · 48 revisions

Writing styles

  1. Prerequisites
  2. File formats
  3. Applying styles to specific sites
    1. Rule types
    2. Syntax
    3. Multiple sites
    4. Advanced matching with Regular Expressions
    5. Valid @-moz-document rules
  4. Preventing global styles
  5. Embed images in styles
  6. Selecting specific elements on a page
  7. Overwriting page styles
    1. Specificity
    2. Inline !important
    3. Replacing images in img tags
  8. Making a companion style

Writing styles

Prerequisites

Writing a user style from scratch requires a working knowledge of HTML external link and CSS external link. It is also very helpful to know how to use external link your browser's DOM inspector external link (usually Ctrl+Shift+C or right-click on an element and choose "Inspect Element"). For those who have limited knowledge of CSS, it's recommended to take an existing style that does something similar to what you want, and attempt to understand and modify it to suit your needs.

File formats

There are two style formats:

Applying styles to specific sites

Styles are applied only to certain URLs defined by @-moz-document external link rules.

In Stylus, these rules are autoconverted into more convenient "applies to" fields which can be controlled via user interface and which can split the CSS code into several sections.

The @-moz-document rules are:

  • Not seen in regular CSS code sections - managing sites is controlled by the Applies to fields in the editor.
  • Visible when pasting a plain CSS style into the editor. A ​Paste the Mozilla-format code modal popup will open. Once applied, the @-moz-document is no longer seen.
  • Always visible in UserCSS styles - managing sites can be controlled by:
    • The Applies to fields, if the Display 'Applies to' info option is set.
    • Modifying the @-moz-document directly in the code.
  • Seen in hosted source code styles (e.g. userstyles.org).

Example Mozilla Format code as you would copy and paste it

@-moz-document url("https://asdf.com/index.html"),
               url("https://asdf.com/guestbook.html") {
  /* This code is for index and guestbook page only */
}

@-moz-document domain("asdf.com"),
               domain("example.com") {
  /* This code is for the whole domain asdf.com
     and example.com including subdomains */
}

@-moz-document url-prefix("https://goodfurniture.com/bookshelves/") {
  /* This code is for something completely different,
     but in the same style */
}

The same style as shown in Stylus

As a plain CSS style

Plain css format

As a UserCSS style (with metadata added)*

UserCSS format


* Note that the @-moz-document is visible in the UserCSS format only.

Rule types

There are four @-moz-document rule types:

  • domain for all URLs on a domain or subdomain (not including the protocol)
  • url-prefix for URLs that start with the exact string (including the protocol)
  • url for exact URLs (including the protocol)
  • regexp for advanced matching with wildcards (including the protocol)

Syntax

@-moz-document rules are specified on the "outside" of normal CSS, just like @media rules. Quotes around the URL are recommended external link. Backslashes inside RegExp must be escaped by another backslash for CSS. Make sure not to forget the closing curly bracket at the end.

In all rules, all letters in URLs are case sensitive, without exception.

A rule to turn the background of https://www.example.com/test.html green would look like this:

@-moz-document url("https://www.example.com/test.html") {
    body {
        background-color: green !important;
    }
}

Multiple sites

Just like you can create a rule in CSS for multiple selectors, you can specify multiple values per @-moz-document block by separating them with commata. For example:

@-moz-document domain("images.example.com"),
               domain('imagehost.com'),
               url-prefix(https://example.com/images),
               url(https://www.example.com/test.html) {
    /*
        The code in here only applies to:
            - Pages on the domains images.example.com and imagehost.com
            - Pages whose URLs start with https://example.com/images
            - The page https://www.example.com/test.html
    */
}

Often, single sites can be accessed on many different URLs and can contain many different kinds of pages. For example, a rule like:

@-moz-document url-prefix("http://www.example.com/")

would not apply to http://example.com/, https://www.example.com/ or http://www.example.com.au/.
You should keep this in mind (and try to account for it!) when writing your @-moz-document rules, especially if you intend on publishing your style for others to use.

Advanced matching with Regular Expressions

Regular Expressions external link (RegExp) offer a powerful way to specify which URLs the style should apply to. RegExp are not recommended when the url, url-prefix, and domain rule types will also do the job, as RegExp are more difficult to understand and more likely to target more than you wanted.

Although there are good external link tutorials external link and pages external link to test external link and analyze external link your RegExp, in most cases it is sufficient to copy and modify the following examples.

The RegExp you write must match the entirety of the URL. This means that ^ and $ (to match the beginning and end of the string) are added automatically. Mind this if you are using lazy quantifiers external link.

RegExp must be escaped using CSS syntax. For example a ? is a control character in RegExp. To match a literal question mark, you would first need to escape it using RegExp rules to \?, then escape that string using CSS rules to \\?. If you are using the "applies to" field, standard RegExp escaping (\?) will be sufficient there.

As / is not used as delimiter character, you do not need to escape it like in other RegExp environments.

Valid @-moz-document rules

Domain

Domain rules should just be the domain name, without protocol, port, or wildcards. A domain rule will affect all pages on that domain and all of its subdomains.

  • Valid examples:

    • @-moz-document domain("example.com")
    • @-moz-document domain("www.example.com")
  • Invalid examples:

    • @-moz-document domain("*.example.com")
    • @-moz-document domain("http://www.example.com/")
    • @-moz-document domain("example.com:80")

URL

URL rules should contain a URL you want to affect, including protocol. Wildcards are not permitted.

  • Valid examples:

    • @-moz-document url("http://www.example.com/page.html")
  • Invalid examples:

    • @-moz-document url("www.example.com/page.html")
    • @-moz-document url("example.com")
    • @-moz-document url("http://www.example.com/*")

Be aware of inconsistent behaviour of html anchors (#):

@-moz-document url("http://example.com/page.html#firstheading") will not match http://example.com/page.html, but
@-moz-document url("http://example.com/page.html") will match http://example.com/page.html#firstheading.

URL prefix

URL prefix rules should contain the start of URLs you want to affect, including protocol. Wildcards are not permitted.

  • Valid examples:

    • @-moz-document url-prefix("http://www.example.com/")
    • @-moz-document url-prefix("http://www.example.")
    • @-moz-document url-prefix("http:")
  • Invalid examples:

    • @-moz-document url-prefix("www.example.com/page.html")
    • @-moz-document url-prefix("example.com")
    • @-moz-document url-prefix("http://*.example.com/")

RegExp

Valid examples:
  • Alternative terms

    @-moz-document regexp("http://www\\.example\\.(com|de|org)/images/.*")

    Applies to URLs that start with:

    • http://www.example.com/images/
    • http://www.example.de/images/
    • http://www.example.org/images/
  • Optional character and an exact URL ending

    @-moz-document regexp("https?://www\\.(example|test)\\.com/")

    Applies just to these four exact URLs:

    • http://www.example.com/
    • https://www.example.com/
    • http://www.test.com/
    • https://www.test.com/
  • Negative lookahead - match everything except any pages on www.example.com

    @-moz-document regexp("(?!https?://www\\.example\\.com/).*")

  • Negative lookahead - match everything except any pages on www.aaa.com, www.bbb.com, and ccc.org

    @-moz-document regexp("(?!https://(www\\.aaa\\.com|www\\.bbb\\.com|ccc\\.org)/).*")

    You can group aaa and bbb since they share www and com:

    @-moz-document regexp("(?!https://(www\\.(aaa|bbb)\\.com|ccc\\.org)/).*")

  • Negative lookahead - match everything except a specific section of a site

    @-moz-document regexp("http://www\\.example\\.com/(?!members).*")

    Applies to all URLs on http://www.example.com/, except those beginning with http://www.example.com/members

Invalid examples:
  • @-moz-document regexp("example")

    would not match http://www.example.com/ or anything URL-like, for that matter.

Sometimes you may encounter RegExps containing URLs with unescaped dots (. instead of \\.). This is technically wrong, but practically might be acceptable because . in RegExp stands for "any character". So using www.aaa.com is unlikely to fail because you probably won't visit a site like https://wwwxaaaxcom.org or https://www.wwwfaaafcom.com.

Furthermore not escaping slashes / when using RegExp in the Stylus context is ok, too.

Preventing global styles

Global styles are styles that will be applied to all sites the user visits. Some style authors will accidentally post global styles to userstyles.org when they intend to post styles that are more specific. If you use the "applies to" controls to limit what site your style is active on, you need to export the style in "Mozilla Format" before copying and posting the code to userstyles.org.

Embed images in styles

If your style requires small images, it's often useful to embed these images inside the style. Doing so eliminates the delay and necessity in downloading the image from a server and gurantees availability.

Data URIs external link can be used to embed images in styles. These URIs include the encoded image, so are very long. To generate data URIs you can use the data URI kitchen external link.

Note that styles posted to userstyles.org are limited to 250 kB, so you will quickly run out of room if you're using large images.

Selecting specific elements on a page

User styles, just like regular stylesheets, select specific elements external link using CSS selectors external link.

When writing HTML, you can modify the markup to more easily apply CSS by including IDs and classes. When writing user styles, you of course cannot control the HTML markup. This means that sometimes you need to get creative to match the elements you want. There are a few strategies you can use:

For example, given this markup:

<table id="prices">
    <tbody>
        <tr>
            <td>Hamburger</td>
            <td>$5.00</td>
        </tr>
        <tr>
            <td>Hot dog</td>
            <td>$4.00</td>
        </tr>
        <tr>
            <td>Taco</td>
            <td>$5.25</td>
        </tr>
    </tbody>
</table>

If you wanted to right-align the second column, you could use this selector:

#prices td:nth-child(2)

Given this markup:

<div class="container">
    <p>Paragraph one</p>
    <p>Paragraph two</p>
    <p>Maybe the number of paragraphs changes per page.</p>
    <p style="float: right">A floating aside on the page.</p>
    <p>More paragraphs...</p>
</div>

If we wanted to hide the floating paragraph, a type selector with a combinator (e.g. .container p) would not work because it's the same element type as the other content (thus would hide all paragraphs).
A structural pseudo-class (e.g. .container p:nth-of-type(3)) would not work because the number of paragraphs is variable.
Instead, we can use an attribute selector:

.container p[style="float: right"] {
    display: none !important;
}

Overwriting page styles

An important consideration when writing user styles is that your style rules often need to override existing rules. Which rule wins if two rules apply to the same property of the same element is determined by CSS cascade rules external link.

It's recommended that all your declarations include the !important keyword. This will give your rules the best chance of applying without additional changes. An example of !important:

a {
    text-decoration: none !important;
}

Specificity

If two rules have the same !importance, the one with the highest specificity external link wins. You can artificially increase your specificity by writing longer selectors (also see specificity calculator external link ). As an example, consider the following HTML markup:

<div id="foo">
    <span class="bar">text</span>
</div>

And the site specifies the CSS:

span {
    color: red !important;
}

You can now make sure that you win by using a more specific selector than the given span, like div > span, .bar, div .bar, #foo .bar, div#foo > span.bar etc.

As an id really boosts the selector's specificity, there's a trick to do it even if the element has no intrinsic id: span:not(#\0). You can increase it further by repeating like span:not(#\0):not(#\0):not(#\0).

In the event of equal importance and specificity, the latest defined rule wins. That is not just inside your stylesheet, but also how and at what point the stylesheet is applied in the HTML. Stylus tries to ensure highest possible specificity for you, but things may vary depending on the browser you use. So if you encounter compability issues that are no cross-browser display errors, try to increase the specificity of the selector.

Using @namespace is obsolete and AGENT_SHEET is not supported.

Inline !important

If you stumble upon an inline style spiked !important, you have no chance of overwriting currently with Stylus (but it may be done by some other extensions that use a different browser API, which we'll eventually implement as well):

<p id="notice" style="text-decoration: underline !important;">
This text will always be underlined.
</p>

In some cases you might bypass it by giving another css property that excludes the unwanted one (e.g. ditch an inline display:block !important by float:left) but in general you need JavaScript external link for that.

You can apply custom JavaScript as UserScript to sites via extensions like Tampermonkey external link or Greasemonkey external link. Something simple like

// ==UserScript==
// @name         Example.com inline important CSS overwrite
// @match        https://example.com/*
// ==/UserScript==

document.getElementById("notice").setAttribute("text-decoration", "none");

should do the job.

Replacing images in img tags

While replacing background images is easy with CSS, replacing images created with <img> tags is not. You need to do the following trick to switch <img> tag images.

#your-selector-here {
    height: 0 !important;
    width: 0 !important;
    /* these numbers match the new image's dimensions */
    padding-left: 125px !important;
    padding-top: 25px !important;
    background: url("http://example.com/your/image/here.jpg") no-repeat !important;
}

Depending on the HTML you have to deal with, other external link techniques external link may be the solution.

Making a companion style

What is a companion style? It's a style that overrides specific portions of an original style.

Why? If there are some things about an existing style that you want to change, it is better not to modify the original style. Because if the style is ever updated (manually only), all your changes will be overwritten by the update and are lost.
Instead, create a new style using the same @-moz-document selectors from the section to be modified. This way your separate style can override the original style while still being able to auto-update the original style.

Still not clear? Let's explain it with examples:

Say you installed the Google - Fixed search bar UserCSS. This style fixates the Google search results header.

/* ==UserStyle==
@name         Google - Fixed search bar
@namespace    https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@homepageURL  https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@version      1.0.0
@license      Other
@description  This theme makes the navigation bar on top of Google searches stick to the top.
@author       DecentM (https://openusercss.org/profile/5a2f0361ba666f0b00b9c827)
@preprocessor uso
==/UserStyle== */
@-moz-document regexp("https?\:\/\/(www\.)?google\.[a-z]{2,3}(\.[a-z]{2,3})?\/search.*") {
  body > div:nth-child(6),
  #searchform,
  #top_nav {
    position: fixed !important;
    width: 100%;
    z-index: 100;
  }
  .hdtb-mn-cont {
    background: #FAFAFA;
    padding-top: 1rem;
    padding-bottom: 1rem;
    margin-top: -1rem !important;
  }
}

It's nice, but the fixed header is too tall. Now what?

Don't modify the original "Google - Fixed search bar" userstyle. Instead, create a new style – create either a regular userstyle or UserCSS, it doesn't matter.

The easiest way to have the companion style target the same pages as the original is to copy the @-moz-document block from the original and paste it into the companion. But if the style has a lot of sections, it might be even easier to duplicate the whole style first to include all the @-moz-document selectors, then remove CSS definitions you don't want to change.

We now have the following copied over to the companion style (with some modifications to the meta data):

/* ==UserStyle==
@name         Google - Fixed search bar COMPANION
@namespace    https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@homepageURL  https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@version      1.0.0
@license      Other
@description  This theme makes the navigation bar on top of Google searches stick to the top.
@author       DecentM (https://openusercss.org/profile/5a2f0361ba666f0b00b9c827)
@preprocessor uso
==/UserStyle== */
@-moz-document regexp("https?\:\/\/(www\.)?google\.[a-z]{2,3}(\.[a-z]{2,3})?\/search.*") {
  /* Insert code here... */
}

Now we can add our customization. Note that you may need to increase the specificity of the selector to override the original style setting.

Here's the final style:

/* ==UserStyle==
@name         Google - Fixed search bar COMPANION
@namespace    https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@homepageURL  https://openusercss.org/theme/5a2f049bba666f0b00b9c82b
@version      1.0.0
@license      Other
@description  This theme makes the navigation bar on top of Google searches stick to the top.
@author       DecentM (https://openusercss.org/profile/5a2f0361ba666f0b00b9c827)
@preprocessor uso
==/UserStyle== */
@-moz-document regexp("https?\:\/\/(www\.)?google\.[a-z]{2,3}(\.[a-z]{2,3})?\/search.*") {
  #searchform {
    top: 10px !important;
  }
  #top_nav {
    top: 60px !important;
  }
}

Now when the "Google - Fixed search bar" style gets updated, we'll still have our customized fixed bar height.