Skip to content

Instantly share code, notes, and snippets.

@belen-albeza
Created July 11, 2018 09:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save belen-albeza/86a8df1ae2a6f7e21a65e17e0131dc3a to your computer and use it in GitHub Desktop.
Save belen-albeza/86a8df1ae2a6f7e21a65e17e0131dc3a to your computer and use it in GitHub Desktop.
CSS guidelines for the Application panel (WIP)

CSS guidelines for Firefox DevTools' Application Panel

A component-based approach

Ideally, we would have a library of re-usable components that encapsulated UI elements for us. So instead of writing <button class="button button-primary>, we would have a <DTButton look="primary"> component that would spawn the former HTML for us.

This is not the case at the time being. However, the approach proposed here would be useful for writing those re-usable components when the time comes.

NOTE: In these guidelines, when we say “component” we are referring to a UI component that might or might not correlate with a React component.

The idea is to follow a BEM-like convention for structuring our CSS rules. What we gain is:

  • A straight-forward naming convention, so there is less hassle thinking how to name a class or write selectors.
  • Coherence along the project.
  • Re-usable styles (since we don’t have a library of React components to achieve this).
  • Low specificity in CSS selectors, which allows for easier maintenance.

Low specificity

Specificity refers to how the browser takes all the rules that affect an element in the DOM and selects which take precedence over those: the more specific a rule, the higher priority it will be given.

For instance, #main-menu is more specific than .main-menu, which in turn is more specific than nav. In the same way, nested selectors will yield a rule with more specificity. Ex: .main-menu ul is more specific than .menu-list.

When there’s an element affected by a rule that is very specific, if we want to override it, it’s usually very hard –we need a more specific selector for that. This makes code harder to maintain. And if we are writing re-usable components, this would make them harder to customise.

We recommend to use the lowest specificity selector that is possible. We can always add a more specific selector later if needed. But doing the reverse –making a selector less specific– is usually more cumbersome and prone to bugs.

In BEM, this is achieved by using class selectors, without nesting –unless absolutely required. For instance, if we have a <ul> list with <li>’s inside, and we want to target the <li>’s, we would have a separate class for those.

Naming conventions

In BEM convention, “nesting” is achieved by using a double underscore __.

For the sake of clarity, classes that are a modifier of an existing component, use a double hyphen --: component--modifier. For instance: button--primary or service-worker--active.

Do:

<nav class="main-menu">
	<ul class="main-menu__list">
		<li class="main-menu__list__item">...</li>
		<li class="main-menu__list__item main-menu__list__item--active">
		<!-- ... -->
	</ul>
</nav>
.main-menu__list {
	padding-inline-start: 0;
}
.main-menu__list__item {
	display: inline-block;
}
.main-menu__list__item--active {
	border-block-end: 0.2rem solid var(--active-color);
}

Avoid:

.main-menu ul {
	padding-inline-start: 0;
}
.main-menu li {
	display: inline-block;
}
.main-menu li.active {
	border-block-end: 0.2rem solid var(--active-color);
}

Yes, the proposed rules are more verbose. But they are also less specific and easier to override in the cascade than using an unneeded, nested selector.

Note that you don’t need to add __subcomponent__ chunks for every element in the DOM if they don’t have associated styles.

Example:

<section class="panel-page">
	<header>
		<h1 class="panel-page__title">Service Workers</h1>
	</header>
	<!-- ... -->
</section>
.panel-page__title {
	font-size: 2rem;
}

And even if the immediate DOM ancestor has styling, it might still make sense to not include it in the naming. Ask yourself: can this component –and its styles– exist outside these ancestor? If so, then it’s independent and should have a name free of their ancestors.

Making the most out of our components

If a more generic component has the styling you need, do not create a new class for it. Given the existing, following HTML and CSS and assuming we want to set the link as disabled:

<article class="service-worker">
	<!-- ... -->
    <a href="#">Open in Debugger</a>
</article>
.link--disabled {
	opacity: 0.5;
}

Do:

<a href="#" class="link--disabled">

Avoid:

<a href="#" class="service-worker__debug-link--disabled">
.link--disabled, .service-worker__debug-link__disabled {
	opacity: 0.5;
}

Pseudo-classes

Pseudo classes should be used with BEM selectors instead of detecting the special state of an element and adding/removing classes with JavaScript.

For instance, for styling the disabled state of a button, the :disabled pseudo-class should be used instead of a regular class selector .disabled and toggling this class via JavaScript when the button is disabled.

Do:

<button class="button" disabled>Submit</button>
.button:disabled {
	cursor: not-allowed;
}

Avoid:

<button class="button button--disabled">Submit</button>
.button--disabled {
	cursor: not-allowed;
}

Pseudo classes to fine-tune selectors such as :not, nth-child, etc. should also be used to avoid write code to undo a previous, less specific rule. By doing this, the cascade is cleaner and easier to debug/maintain.

For instance, given this HTML:

<nav class="menu"><ul>
	<li class="menu__item">...</li>
	<!-- ... -->
</ul></nav>

Do:

.menu__item {
	display: inline;
}

.menu__item:not(:first-child) {
	margin-inline-start: 1rem;
}

Avoid:

.menu__item {
	display: inline;
	margin-inline-start: 1rem;
}

.menu__item:first-child {
	margin-inline-start: 0;
}

Another example:

Do:

<article class="line"><!-- ... --></article>
<article class="line"><!-- ... --></article>
<article class="line"><!-- ... --></article>
.line:nth-child(2n + 1) {
	background: var(--bg-alt-color);
}

Avoid:

<article class="line line--odd"><!-- ... --></article>
<article class="line"><!-- ... --></article>
<article class="line line--odd"><!-- ... --></article>
.line--odd {
	background: var(--bg-alt-color);
}

Units

Preferred units:

  • rem and em over px

TODO: clarify what is our root size (in pixels) in the panels (also in Linux).

Viewport units (vw and vh) are also useful to make sure that elements take its size depending on the viewport, and not its parent –for instance, <body> doesn’t stretch to the bottom of the screen unless needed!

Example:

.main-wrapper {
	min-height: 100vh;
	display: grid;
	/* ... */
}

Layout

Positioning and laying out elements with position: absolute and/or display: float should be avoided, and use modern CSS modules, like Grid and/or Flexbox.

Grid vs Flexbox

It’s often said “Use Grid when you are working with two dimensions, use Flexbox when you are working with one dimension”, but this is over simplistic and doesn’t reflect with what each API is good for.

In most cases, Grid is what we want. It doesn’t matter if we are dealing with just one row or one column. Grid makes much easier to set the dimensions for our layout, using units like fr or functions such as min-max (plus gaps, etc.). It also has the advantage of defining which parts grow or shrink in the grid itself, instead of in the individual elements, which makes the styles easier to maintain.

When to use Flexbox, then? Whenever we need to distribute elements along a line (it doesn’t matter if this line spans over multiple lines, and thus appearing “bi-dimensional”). For instance, when we need to distribute elements equally with space-around or space-between.

Remember that we can use Flexbox inside a Grid cell, too! They are not mutually exclusive.

Adapting to different viewport sizes

  • minmax, fr
  • media queries

Adapting to different text directions

Advice

Avoid using a 3rd party framework

Full-fledged frameworks such as Bootstrap implement a particular design with a particular methodology. For custom designs –such as ours–, the cost of having to override practically everything to adapt the style rules to the custom design makes these types of framework not worth it.

About layout frameworks (for instance, those which implement a grid), they are not necessary anymore with CSS Grid.

Get advantage of currentColor

currentColor maps to the value that color is resolved for this element. So, for instance, if we need to add a border that is the same color of the text, we could just use currentColor instead of repeating the same RGB / variable values.

Also, if further in the cascade the text color is changed by another rule, currentColor will reflect that.

Example:

.btn {
	border: 0.1rem solid currentColor;
}

.btn--danger {
	color: #f00; 
}
<!-- this button border color will be #f00 -->
<button class="btn btn-danger">Delete</button>

When to use !important

Generally speaking, !important should be avoided, since it changes how the cascade works and makes debugging and/or refactoring more difficult.

However, there are a (very, very, very) few cases in which the use of important might be justified –normally for utility classes such as .hidden. From CSS Guidelines:

Proactive use of !important is when it is used before you’ve encountered any specificity problems; when it is used as a guarantee rather than as a fix.

Example:

.hidden {
	display: none !important;
}

Don’t use units with line-height

Line height should be relative to the font size. When not using units, this is achieved, so when set in the body, it will usually yield a good baseline style for all elements –which can be customised further if needed in more specific rules.

body {
	line-height: 1.5;
}

Use text-transform to capitalize text

Often the capitalisation (or other transform, such as small caps, or lowercasing), belong to the design itself. When the design changes, the text that used to be capitalized might not be this time.

To separate markup from styling, follow the normal language spelling rules and apply a text transform via CSS.

Do:

<button class="btn">I agree</button>
.btn {
	text-transform: uppercase;
}

Avoid:

<button>I AGREE</button>

How to style even/odd elements

How to vertically center an element

You can vertically align easily with Flexbox and Grid, without requiring a transform, or without hardcoding the dimensions of the element to be aligned.

Do (by creating a new grid):

.container {
	display: grid;
	align-items: center;
}

Do (by setting align-self to an element inside a grid cell):

.container__item {
	align-self: center;
}

Do (by using Flexbox):

.container {
	display: flex;
	align-items: center;
	/* Note: use `justify-content: center` instead if `flex-direction` is set to `column` */
}

Note: other values of align-items allow us to align to top, bottom, etc.

Avoid:

/* Why: - We are creating a new z-index stack.
    - Transforms can sometimes yield blurry results.
        - We are spreading the alignment into two different
          rules.
*/
.container {
	position: relative;
}

.container__item {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
}

Avoid:

/* Why: - We are creating a new z-index stack.
        - We need to know in advance & hardcode the dimensions
          of the element we are aligning.
        - Hard to maintain / prone to disalignment if
          dimensions change.
*/
.container {
	position: relative;
}

.container__item {
	position: absolute;
	top: 50%;
	height: 300px;
	margin-left: -150px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment