Introduction to campaign CSS

Campaign CSS is a set of CSS rules (Wikipedia) that allows you to customize the appearance of your campaign for every visitor. If you have no idea what CSS itself is, I recommend familiarizing yourself with it first, since this guide is more about how to use CSS on Kanka, and not about teaching CSS itself. The W3C tutorial can be a good place to start. This article will suggest links to specific concepts you may not already be familiar with.

To edit your campaign’s CSS, go to Settings from the main navigation to access your campaign settings. There, under the Customisation menu, click Theming. You can create a new style using the + NEW STYLE button or add to an existing one. You can create up to 30 separate stylesheets to compartmentalize your CSS rules into logical groups, making them easier to locate and edit later on.

CSS priority

One thing to keep in mind is that in CSS, when conflicting rules are found, there are two ways the browser chooses which one to apply. The most important one is specificity: the more specific a rule, the more importance it is given. This W3C page explains it simply while detailing the "weights" of various types of declaration, so I won’t rehash that here.

This prioritization is especially important when you attempt to style elements whose appearance is already set explicitly by Kanka’s code, or by Marketplace themes you’ve installed. If Kanka has a built-in rule for p {}, you will need a rule that is at least equally specific to override it (for example, p {} or body p {}).

The second way CSS rules are prioritized is by order of appearance: if two rules are equally specific, the browser will apply the last one it finds, assuming that it is meant to override an earlier declaration. Suppose you have a CSS file that looks like this:

p { color: black; }
p { color: red; }

Then your paragraph’s text will be red, since specificity between the two rules is equal. We need to be aware of this because Kanka uses multiple stylesheets that it loads in a specific order, chosen specifically to leverage this ordering logic. When it comes to customizing your campaign, the cascading order you need to keep in mind is as follows:

  1. Kanka’s generic stylesheets (from frameworks, libraries and Kanka itself)

  2. Content-specific stylesheets (such as dashboard.css for the Dashboard or the Leaflet stylesheet for the maps module)

  3. The active theme’s stylesheet, if other than Default

  4. campaign_plugin-<cache ID>.styles for Marketplace themes (<cache ID> is a series of a digits that is updated several times a day)

  5. campaign-<cache ID>.styles for campaign CSS

  6. High-priority styles like Font Awesome

As you can see, this means that, at equal specificity, 1) Marketplace themes override Kanka themes, and 2) your campaign’s CSS overrides both. This is great because it means you can build a community theme on top of a specific Kanka theme, and you can install Marketplace plugins to do most of the work for you but still tweak some rules to your liking. It’s also a reminder that your customizations may look different on certain pages such as the Dashboard, because specific stylesheets are being loaded that aren’t used elsewhere.

It’s worth noting that you can cheat with priority by marking a rule as important, like so:

p { color: black !important; }

This will supersede the priority check, but it is considered bad practice and should only be used as a last resort (for example if you need to override inline styling on an element, and you can’t make a declaration specific enough to take precedence over inline styling). If you end up having multiple conflicting !important rules (a sign that things are really getting out of hand), specificity and order of appearance will once again be called into action to choose among them.

Screen size

As is common practice, Kanka applies different CSS rules based on the user’s screen size. While that doesn’t matter to you if you’re only changing the color of an element, it can lead to undesirable results when you change the sizing or layout of entire blocks. Fortunately, Kanka keeps things simple most of the time with two main formats: 767px-wide or less, and 768px-wide or more. When you need to make changes that should only affect one of those views, wrap your declarations within a @media rule as appropriate:

/* Change background color on small screens */
@media (max-width:767px) {
  .wrapper {
    background-color: blue;
  }
}
/* Change background color on large screens */
@media (min-width:768px) {
  .wrapper {
    background-color: blue;
  }
}

Tip: Indenting your code will help you remember to close that extra set of brackets.

One thing that can lead to unexpected behaviour is that Kanka also uses JavaScript to alter elements based on screen size or user interactions, so you may need to inspect elements in various states to find out how exactly they can change dynamically and account for those changes. One example is the sidebar-collapse class, detailed below.

Practical tips

Dev tools

Now is a good time to introduce you to your browser’s developer tools, which are extremely helpful when working with CSS. These tools allow you to inspect elements on the page to see their classes and ID, see exactly how the HTML is structured around them, what CSS rules apply to them (including ones that are overridden by specificity or sequence) and what files those rules come from. They also include extremely convenient features that allow you to fake states such as hovered or active, light/dark theme user preferences, printing mode or mobile device resolutions. Last but definitely not least, you can edit, disable and write additional CSS rules or entire stylesheets directly on your live page to test changes without going back and forth editing and saving your campaign styles and refreshing a page to see the results.

Chrome and Firefox use essentially identical dev tools, so here is a link to Chrome’s exhaustive guide to dev tools. Other popular browsers are likely to have similar features available.

Query parameters

There are a few parameters you can add to Kanka URLs (a part of the URL called the query string) to turn on or off certain styles and speed up your testing and exploration. First, note a couple rules that apply to all URL query strings:

  1. The first parameter is always preceded by a question mark: .../overview?param=value.

  2. Additional parameters are always preceded by an ampersand: .../overview?param1=value&param2=value.

  3. Therefore, whenever someone refers to query parameters, whether they mention &param or ?param or just param, you should prefix them with the appropriate character based on your specific URL.

?_theme: Loads the page in a specific theme regardless of user or campaign preferences. Possible values are "base", "dark" and "midnight".

?_plugins=0: Loads the page without the campaign_plugin.styles stylesheet, disabling all Marketplace themes.

?_styles=0: Loads the page without the campaign.styles stylesheet, disabling all campaign styles.

Themes and custom properties

Kanka proposes a few core themes (Base, Dark and Midnight Blue) out of the box. Boosted or Premium campaigns can force one of these themes for all visitors, but you can also let Kanka use whichever theme a user has selected in their profile (or Base by default for logged out visitors). If you go for the latter option, it may be tricky to set certain colors in your campaign CSS, since they might not suit the active theme. The solution? CSS custom properties.

Those properties are like variables set by each Kanka theme and which you can refer to in your campaign CSS instead of imposing a preset value. For example, the official Dark theme used to set --theme-main-text: #A4A4A4;. If you wanted to make an element that normally used a different color match the active theme’s main text color, you could therefore use color: var(--theme-main-text); on that element instead of giving it a specific color that might not look right in other themes.

Why the past tense? Well, the introduction of the Theme Builder led to the removal of most of the built-in custom properties and replaced them with obscure ones like --n and --sf. Technically, you can still reference them, but that will force you to use the HSL color format. The official documentation provides a reference of what elements each color is used on. Some parts of Kanka still use the old custom properties if set by a campaign or plugin’s CSS, but you will have to investigate using dev tools to figure out which ones and where.

When you override a custom property’s value, you can do so globally at the :root level or on more specific elements; for example, in entities of a specific type:

:root {
  --theme-main-text: darkblue;
}
.kanka-entity-character {
  --theme-main-text: green;
}

This allows you to change a variable’s value once and instantly affect every element that refers to it, instead of identifying and targeting all such elements individually.

Context-specific classes

Kanka probably uses hundreds of CSS classes, so we won’t go over all of them here, but there are a few important ones that you will likely want to make use of. The following are classes that change depending on the content you are viewing, or the state of certain elements on the page, and can help you style things conditionally.

Entity classes

Several very useful classes can be present on the body element:

Entity type: Indicates the type of entity being viewed, for example kanka-entity-journal for a Journal. Similarly, each of the main entity lists has a similar class such as kanka-abilities or kanka-characters.

Custom type: Indicates the type assigned to an entity, for example kanka-type-npc on a Character given the "NPC" type. Note that it is all lowercase and spaces are replaced with hyphens.

Entity ID: Indicates the unique ID of a specific entity, for example kanka-entity-1.

Tags: An entity’s tags are identified in two formats, namely their unique IDs and their names in "slug" form, for example kanka-tag-245634 kanka-tag-classabilities for a tag named "Class Abilities". The surest way to have the right spelling of those slugs is to inspect the body of a relevant entity.

entity-story: Indicates that the current page is the main Overview page of an entity, as opposed to its secondary pages or other parts of Kanka. Most subpages also have a similar class to identify them — entity-attributes, entity-abilities, entity-relations and so on.

Some examples of custom selectors

Those classes allow you to define rules that apply to all entities of a given type, or to customize a unique entity very specifically. The easiest way to ascertain the name of the class you are looking for is to look at the page’s source code in your browser and check the body tag’s classes. Below are a few examples of custom selectors you could make for various scenarios. I will use two of my own test entities to demonstrate, with the following body tags:

<!-- Overview page of a Timeline with two tags -->
<body class="kanka-entity-1955453 kanka-entity-timeline kanka-tag-166712 kanka-tag-horizontaltimeline kanka-tag-91036 kanka-tag-test  entity-story" >
<!-- Attributes page of an Ability with one tag -->
<body class="kanka-entity-1202673 kanka-entity-ability kanka-tag-91036 kanka-tag-testtemplate  entity-attributes" >

Target a specific entity by ID:

.kanka-entity-1202673 { ... }

Target all entities with a certain tag (by tag slug):

.kanka-tag-horizontaltimeline { ... }

Target only the Attributes subpage of all entities with a certain tag slug:

.kanka-tag-test.entity-attributes { ... }

Target all entities of a certain type (Ability) that don’t have a certain tag slug:

.kanka-entity-ability:not(.kanka-tag-test) { ... }

Target a few types of entities that match a certain tag slug, except one specific entity:

.kanka-tag-test:is(.kanka-entity-ability, .kanka-entity-note, .kanka-entity-creature):not(.kanka-entity-1202673) { ... }

Target all entities with any tag that starts with "test" (for example Test and Test Template), excluding their Connections page:

body[class*="kanka-tag-test"]:not(.entity-relations) { ... }

Styling posts

Since Kanka 1.39.2, you can use the CSS class field on individual posts to effectively tag them as targets for custom CSS. For example, you could use this to give a standard style to a "Heraldry" post that you add to various organisations and families, without affecting other posts or entries. Your space-delimited classes will be added to the outermost container of the post, so you could target all of those posts regardless of which entity they appear on with something like .entity-posts .my-css-class {...}.

Mention attributes

Similarly, entity mention links include various HTML attributes that can be used in CSS selectors to style them based on what they link to. All of them also have the entity-mention class.

data-id: Indicates the unique ID of the target entity. Example: data-id="12345", which could be targeted with .entity-mention[data-id="12345"] {...}.

data-entity-tags: Contains the ID and slug of each tag applied to the mentioned entity. Note that these slugs don’t necessarily use the same replacement logic for spaces and special characters as the tags in the body’s tag classes, so it’s best to inspect a mention to find out the correct slug. Example: data-entity-tags="id-41414 sandwich-recipes", which could be targeted with either of:

  • .entity-mention[data-entity-tags~="id-41414"] {...} (inclusive word match ~=: this specific tag ID — and not "id-414141", for instance)

  • .entity-mention[data-entity-tags*="recipes"] {...} (partial match *=: any tag that contains the word "recipes", including "badrecipes" and "goodrecipes")

  • .entity-mention[data-entity-tags="id-41414 sandwich-recipes"] {...} (exact match =: this specific tag and no other)

  • etc.

For more details on attribute selector syntax, see MDN.

Note that other elements in Kanka may be similarly identified, such as individual abilities in the Abilities subpage of an entity, allowing you to use tags to apply different styles to different categories of content of a similar nature. More of these are added over time as requested, so use the browser’s dev tools to inspect the element you want to style and see if its tags are identified, and otherwise feel free to submit a feature request to have them added.

Sidebar state: When the sidebar is closed, the class sidebar-collapse is applied to the body tag. When it is open, no class is applied (so you can use body:not(.sidebar-collapse) {...} to target that state).

User privileges: A class exists on the element aside.main-sidebar that indicates whether the user is a logged-in member of the campaign: main-sidebar-member or main-sidebar-public. This allows you to hide or show certain elements accordingly, for example:

/* to hide sidebar elements from public viewers (for example, quick links that are only useful to your players) */
.main-sidebar-public .sidebar-quick-link-1 {
  display: none;
}
/* to hide or alter other interface elements that aren't as useful if someone isn't logged in, such as entity sidebars */
.main-sidebar-public ~ .content-wrapper .entity-menu-2 {
  display: none;
}
/* conversely, to add or restyle content for your members' eyes only */
.main-sidebar-member ~ #campaign-dashboard .campaign-title::after {
  content: "– the best campaign ever!";
}

Other useful classes and attributes

  • entity-content is used in all places that display the entry or post content of an entity, such as: the entity’s Overview page, entity preview widgets on the dashboard, map sidebars in Explore mode, timeline eras and elements, etc. It is very useful for consistently styling basic elements such as paragraphs and lists in user-generated content across all of Kanka without affecting the general interface.

  • is-admin is added to the body tag when the active user is the campaign’s administrator. You could use this to hide content from most users but keep it visible to yourself, or simply use it as a way to style things differently for yourself only. Just keep in mind that a nosy enough person could add the class to a live page manually, and that content hidden with CSS is still "physically" present on the web page, just not displayed until instructed otherwise.

  • data-user-member is an HTML attribute on the body tag that indicates whether the user is a logged-in member of the campaign, so you could target it with body[data-user-member="1"] {...} for members or body[data-user-member="0"] {...} for others.

Using custom fonts

A popular way to customize campaigns is to use different fonts than the Kanka defaults. There are two ways to do so: uploading them to your campaign Gallery and defining them using CSS @font-face rules, or importing them from services such as Google Fonts using CSS @import rules.

  1. First, you will need to use a WOFF2 file as that is the only font format currently allowed in the Gallery. There are various websites that can convert font files for you if yours is in a different format. During this step, you should also ensure that your font’s licensing allows you to use it on a public website and check whether it requires attribution.

  2. Upload your font file to the Gallery, then click its thumbnail to view its details. Near the bottom, you will see a link to the file, whose URL you need to copy (it will be a very long amazonaws.com URL).

  3. In any of your campaign’s styles (accessible via the Theming page of your campaign settings), add a @font-face rule pointing to that URL and give it any name you want. You can then use that font anywhere in your campaign’s styles with the font-family property (no need to define it in every style!).

@font-face {
	font-family: ILikeThisName;
	src: url("nobody-has-time-for-long-urls.woff2");
}

.this-class-is-special {
	font-family: 'ILikeThisName', serif;
}

Last updated