User:Esbylion/Citizen Primer

From Final Fantasy XIV Online Wiki
Jump to navigation Jump to search

This is a quick(ish) guide on how to work with Citizen, mixed with some CSS basics that you may not need. If you do need those and want more, you can read a comprehensive, excellent tutorial on CSS basics here. Remember, at any time you can make non-destructive changes to a page's styling by adding CSS rules to elements via the dev tools in your browser (hit F12 and make sure you're in the Inspector tab, or right-click > inspect element). You may need to be on a non-mobile device to do this.

How do I change a specific element?

A CSS rule is made up of two parts - the selector, and any declarations that apply to it. The following example turns any <h1> element red.

h1 {
  color: red;
}

Here, the selector is 'h1', and the declarations are contained within curly braces { like this }. If you're used to editing wikis, you are probably familiar with CSS rules being applied to specifc elements, like so:

<h1 style="color: red">

This is 'inline' styling, and is one way to style a specific element - simply adding the CSS declarations into the Wikitext directly. However, this means the rule is applied in all situations, skins, color schemes and layouts, meaning you cannot change the rules based on whether a user is in light or dark mode, or if they're viewing the page on mobile. For those and several other benefits, a separate 'stylesheet' must be used.

On a wiki, Mediawiki:Common.css is the main editable stylesheet. Its rules are applied to every page, regardless of skin or layout, but unlike inline styling, we have access to selectors. This allows for much more efficient styling. For example, if we have multiple <h2> elements on a page, and we want them all to be blue, we don't have to set style="color:blue" on every single one manually. Instead, we can set up another one of these in that stylesheet.

h2 {
  color: blue;
}

Here, the selector is "h2", meaning any <h2> element, and the declaration is that the "color" property should have a value of "blue". We separate property and value with a ":", and separate declarations with a ";". If your CSS rule isn't working, you may have forgotten a colon somewhere.

selector {
  property: value
}

As an aside, in the case of editing Citizen, you will want to use Mediawiki:Citizen.css. This ensures that none of your changes affect other skins.

Lets say we have a bunch of <h2> elements, and we only want one of them to be blue. Our selector needs to change, and here's where our options open up. If we set the wikitext like this,

<h2 class="blue-heading">

We add a class, "blue-heading", to our element. Now, we can set our CSS rule to select that class by putting a period '.' in front of it.

.blue-heading {
  color: blue;
}

Any element with the class 'blue-heading' will now have the 'color: blue' rule applied to it. If you inspect the element below, for example, you will see a great deal of rules just like this. Any Mediawiki skin will be applying a great deal of these rules (along with your browser) in order to style the page you're being shown.

Inspect this element

You can (presumably) see the highlighted <h4> element. It has an id of "Inspect_this_element". We can select this id using a CSS selector too, using a # instead of a period, like so.

#Inspect_this_element {
  color: blue;
}

An id is similar to a class, but is understood to be unique on a page. For Wikitext CSS purposes, it doesn't offer many advantages over a class, but Citizen and other skins do use them, so feel free to use them as targets.

That <h4> element is inside a <div> with class="mw-heading mw-heading3" attached to it. This <div> element is the <h3> element's parent. The parent-child relationship is often important in CSS, as children can be made to inherit many qualities of their parents, but that's beyond the scope of this tutorial. This part of the MDN tutorial goes into more detail. The main thing that should be understood is that unless otherwise specified a child's position will be contained within its parent, so if you want to change an element and selecting it directly isn't working, you may need to look at the parent's CSS rules.

Okay but how do you change a specific element

So, to recap, let's say we want to change the background color of this page. First, we need to identify the relevant element. This can involve a little trial and error, but using the dev tools to do this is non-destructive. Go ahead and open those up, unless you're on mobile, in which case, idk, skip this part.

In our case, we are looking for the element that contains everything in the page, so we can start near the top of the document (or right click anywhere on top of our background, and follow that up).

  • The <html> element is generally not where this kind of styling is applied (though we may use that for setting custom properties, which we'll get to shortly).
  • The <body> element is a safe start in some cases, but here includes things like the header, and various other functional Citizen elements.
  • If we mouse over the <div> element with the class "citizen-page-container" in the dev tools, we can see from the highlight on the page that it covers pretty much everything but the header. Additionally, since every other visible element on the page is a descendant of this element, it's a good place to try adding a rule that we want to affect the entire page.

Let's ignore selectors for now, and just make a change. Depending on your browser, you should see a 'This Element' heading in the right column, with an empty 'element { }' rule beneath it. Make sure you're in the Inspector tab, and the right column is in the 'Rules' tab. If you have it, you can insert any declarations you like into that empty rule without affecting anything but your current view of this page. Refreshing will wipe any rules you've set this way, and nobody else will ever see them, so have fun.

If we want the color of the background to change, we use the 'background-color' property. We can also use the 'background' property, but that also accepts a lot of other values, whereas 'background-color' is just for solid colors. We'll get into how to represent color in a minute, but this is a safe, quick way to test changes you want to make.

Once you've found a color you like, all you need to do to add it to the website properly is edit the relevant stylesheet to include a rule that selects the element and adds the declaration. In our case, we select using the element's class (with a period . in front), like so

.citizen-page-container {
  background-color: white;
}

Pretty much every element in Citizen (or most skins) will have a class or id you can use to select it, but there may be some cases in which you need to be more specific. Let's talk a little more about selectors.

Selectors

Citizen has a few dropdown menus. If you want to style, for example, an element with the class "citizen-menu__card-content", you will notice that using that class as a selector will apply any declarations within to every element that has that class - that is, every dropdown menu. If you don't want this, and just want to change that element in a single menu, you need to expand your selector.

#page-actions-more__card .citizen-menu__card-content {
  background-color: white;
}

By listing two selectors with a space between them, we are telling the stylesheet to select any elements with the "citizen-menu__card-content" class that are descendants (children, or children's children, etc.) of the element with the "page-actions-more__card" id. This allows us to limit our rule to just the element we want to see it on.

There's many other things you can do with selectors, depending on the complexity of changes you want to make. Check out a big ol list here.

Color

There are whole bunch of ways to define a color in CSS. There's a list of named colors, as well as the conventional hex color format (#fcc79b). Realistically, anything you want to use will probably be fine, but Citizen primarily relies on Oklab, because Oklab rules. To keep it short, Oklab is a 'perceptual' color space, meaning that it defines colors with numbers that reflect how we see them relative to other colors - increasing the 'lightness' makes a color 'lighter, etc. You may be familiar with HSL color, another common perceptual color space. If it helps, you can think of Oklab as HSL but with less fiddling about with the levers.

Like HSL, Oklab colors have three numbers - Lightness, Chroma, and Hue. In CSS, these are defined in that order using an oklch() value, like so;

h2 {
  color: oklch(0.9 0.05 80);
}

You can see what this color looks like, as well as pick your own colors, with this tool. It's fun!

Out of the box, Citizen derives all of its color from a single Hue value. By setting the '--color-progressive-oklch__h' property to a value from 0-360, we can make drastic changes to the color of Citizen pages. All it's doing is taking that Hue value, and applying it to the end of various oklch() values using custom properties (which we'll get into next). Thanks to Oklab, changing just the lightness (and sometimes chroma) values in sensible increments makes for visually appealing color contrasts. To be clear, there is no requirement whatsoever to use Oklab over anything else. Oklab just rules.

For backgrounds, the final oklch() value is applied to various elements using custom properties.

Custom properties

These are really, really useful. Let's say we want to use the same color value across multiple different properties - we want the border of one element to match the text color of another. We could do this,

.border-example {
  border-color: oklch(0.9 0.05 80);
}

.text-example {
  color: oklch(0.9 0.05 80);
}

But, if we want those two properties to stay the same, we have to change both of those values, every single time. Plus, it gets more complicated when things are across multiple stylesheets. Instead of all that mistake-prone, tedious copy-pasting, any time we know that we want to elements to always be the same color, we can use a custom property.

:root {
  --example-color: oklch(0.9 0.05 80);
}

Custom properties have two dashes in front of them, and are usually best set using the ':root' selector, if possible. This ensures it applies to all elements, wherever they are in the page. Once we've done that, we can use that custom property into any CSS rules we want, and it will automatically substitute whatever value was set.

.border-example {
  border-color: var(--example-color);
}

.text-example {
  color: var(--example-color);
}

Then, if we later want to change that color (or have it change automatically with the color scheme, for example), we only have to change it in a single place. Don't forget to put the custom property in a var() when you're using it as a value!

Citizen has a host of these custom properties ready to go; --color-surface-0 through to --color-surface-4 are the main 'background' colors, while --color-base sets the body text, and --color-progressive is the main accent color. These can be used anywhere, and can also be modified with the :root selector above. For example, if you want your article's body text to be blue, along with anything that's meant to match it, rather than setting every element to "color: blue" individually, combing over every relevant page until you've found them all, you could just take advantage of custom properties.

:root {
  --color-base: blue;
}

Isn't that easier? You should always look for opportunities to set colors with custom properties. It'll be easier to set up, easier to change, and ensures your work can be responsive to color scheme changes. Plus, you can also make temporary changes to the values of custom properties in the dev tools, so you can quickly see what any big changes would be like.

Spacing properties

Another handy thing that Citizen has built in is a series of 'space' properties. These all multiply a base '--space-unit' property to create a series of comfortable, handy gap sizes. Using these for any small padding or margins keeps things tidy and consistent, as well as making it easy to change.

--space-xxs: calc(0.25 * var(--space-unit));
--space-xs: calc(0.5 * var(--space-unit));
--space-sm: calc(0.75 * var(--space-unit));
--space-md: var(--space-unit);
--space-lg: calc(1.25 * var(--space-unit));
--space-xl: calc(1.5 * var(--space-unit));
--space-xxl: calc(2 * var(--space-unit));

'--space-unit' is 1rem by default.

Dark and light mode

Let's say that we have a 'white' background, and we want it to change to 'black' when the user switches to dark mode. This is slightly fiddly, but we can bring together everything we've established above to make it easy to work with. Ultimately, setting a color to have both a dark and light mode in Citizen could look something like this;

:root {
  --light: white;
  --dark: black;
}

.skin-theme-clientpref-day {
  --color-surface-0: var(--light);
}

.skin-theme-clientpref-night {
  --color-surface-0: var(--dark);
}

@media (prefers-color-scheme: light) {
  .skin-theme-clientpref-os {
    --color-surface-0: var(--light);
  }
}

@media (prefers-color-scheme: dark) {
  .skin-theme-clientpref-os {
    --color-surface-0: var(--dark);
  }
}

.targeted-element {
  background-color: --color-surface-0;
}

Now, for any elements we want to share a background color (like, say, the page background and header), we can simply apply the declaration "background-color: var(--color-surface-0)", and any time we want to change both of those elements together, we only have to do it one place. Here's how it breaks down;

First the selectors. Citizen adds a '.skin-theme-clientpref-*' class to the <html> element, depending on whether or not the user has set Auto, Light or Dark as their color scheme preference (with Auto being the default). We can use this class to set our custom color properties, and they'll apply through the rest of the document, as the <html> element is the parent of everything else on the page.

Then, assuming we want Auto to match the other two using the user's browser preference, we need a way to know what that browser preference is. This is what '@media' queries are for. CSS has a number of queries it can make to the browser, including useful things like device width, or in this case, the preferred color scheme. We can open a rule with a media query to say that the rule only applies if the query is true - in this case, 'prefers-color-scheme: light' means the browser either has no preference, or has specifically requested a light mode, while 'prefers-color-scheme: dark' means the browser is specifically requesting dark mode.

After we've checked what the browser wants, we need to select the '.skin-theme-clientpref-os' class, as that indicates the user has Auto selected in their wiki color preferences. However, as we just want the Auto preference to match the Light and Dark preferences, we can use custom properties again to set each of those colors once with the :root selector. The browser applies the value attached to --light to --color-surface-0, which in turn applies the value to any properties we use --color-surface-0 on.

Further reading

There are a very large amount of properties available in CSS, which MDN covers pretty comprehensively, but much of the basics will come down to margins and background-colors. There's a whole lot of stuff I didn't cover, so I'm happy to clarify anything, correct any errors, or anything else to do with this, so don't hesitate to ask questions.