-
Notifications
You must be signed in to change notification settings - Fork 306
Writing styles
- Prerequisites
- File formats
- Applying styles to specific sites
- Preventing global styles
- Embed images in styles
- Selecting specific elements on a page
- Overwriting page styles
- Making a companion style
Writing a user style from scratch requires a working knowledge of HTML and CSS . It is also very helpful to know how to use your browser's DOM inspector (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.
There are two style formats:
- The classic one with plain CSS (can be uploaded to userstyles.org as "Mozilla Format")
- The UserCSS format, which additionally allows style options, preprocessing (LESS / Stylus-language );
The file can be either self-hosted (including GitHub, Dropbox, etc.) or uploaded to userstyles.world ;
See Writing-UserCSS for more info.
Styles are applied only to certain URLs defined by @-moz-document
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
As a UserCSS style (with metadata added)*
* Note that the @-moz-document
is visible in the UserCSS format only.
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)
@-moz-document
rules are specified on the "outside" of normal CSS, just like @media
rules. Quotes around the URL are recommended . 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;
}
}
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.
Regular Expressions (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 tutorials and pages to test and analyze 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 .
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.
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 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 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/")
-
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
, andccc.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 withhttp://www.example.com/members
-
@-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.
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.
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 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 .
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.
User styles, just like regular stylesheets, select specific elements using CSS selectors .
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:
- Use combinators to start from an element that does have an ID or class (See specifications) .
- Attribute selectors work well if the element has a unique combination of attributes (See specifications) .
- Structural pseudo-classes like :nth-child() select elements based on their position in the document (See specifications) .
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;
}
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 .
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;
}
If two rules have the same !importance, the one with the highest specificity wins. You can artificially increase your specificity by writing longer selectors (also see specificity calculator ). 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.
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 for that.
You can apply custom JavaScript as UserScript to sites via extensions like Tampermonkey or Greasemonkey . 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.
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 techniques may be the solution.
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.