From the translator:
The article was written on behalf of Mark Otto , one of the leading maintainers of the popular front-end Twitter Bootstrap framework, now a CSS developer on GitHub .
I have always been interested in the process of developing various applications, especially their styled guides and their approach to developing CSS. Considering my tendency to sometimes meaningless details in the development of CSS, I decided to write a little about the process of developing CSS in GitHub.
a brief description of
An overview of the current status of the CSS code shows:
')
- Using SCSS as a preprocessor;
- We have over 100 separate source stylesheets, which we compile before being released to production;
- Compiled CSS is divided into 2 files to avoid a limit on the number of selectors in IE versions <10;
- These two files have a total weight of 90KB;
- We do not adhere to any particular approach in the CSS architecture;
- We use pixels as a unit of measurement, although sometimes we also use em;
- We use Normalize.css and blending our own drop styles.
Preprocessor
As stated above, we use SCSS. This choice was made long before my arrival and I have nothing against it (despite the fact that Bootstrap is being developed at LESS). Our SCSS files are compiled by Ruby on Rails with some help from Sprockets to include files. More on this later.
What about LESS, Stylus, or ...? I don’t think that GitHub had once planned to switch to LESS, but I can’t say it. Now we also, most likely, will not switch to development using LESS, since We see no clear advantages.
Why do you even use a preprocessor? Our internal framework includes a relatively small set of variables (such as blending fonts or primary colors) and mixins (most often for properties with vendor prefixes), which makes code development faster and easier.
At the moment we do not use Autoprefixer, but, in fact, we should, because in this case, almost all our mixins will not be needed. Hope we will realize it soon.
We are also not currently using
source maps , but this will change soon. (If you didn’t know, source maps let you see in the browser's Inspector which source SCSS file the given style set was compiled for instead of compiled and compressed CSS. They are cool.)
In addition to this, we use very few SCSS features. In our case, the SCSS functionality is reduced to the use of variables, mixins, color functions (
darken, lighten , etc), mathematical functions and inheritance.
Architecture
At the moment, two popular approaches to CSS architecture are
BEM and
OOCSS . We are leaning towards OOCSS, but we do not have a holistic and global approach. We try to develop new elements with a vague approach combining the properties of these two approaches, but which has the following basic features:
- Preference to classes, rather than anything else (ID, tags), in selectors;
- Avoiding unnecessary inheritance;
- Using (single) hyphens in class names;
- We try to give the shortest possible names to classes, but without creating confusion and confusion.
I will write more about my preferred CSS architecture in another post. Now the text above sums up the approach of GitHub, which, of course, is not perfect, but copes well with what is required of it.
Syntax check (linting)
We started using the
syntax check of our SCSS a few weeks ago. We had our own conditional agreements regarding the code-style, but the style and formatting of each developer was to some extent unique. Now, each CI build includes the base SCSS linting and the file is not released in the build if:
- Your class is in CSS, but it is not found anywhere in the app / views template folder;
- You use the same selector several times (since they should almost always be combined);
- The general formatting rules (inheritance limits, indents between rules, no spaces after : etc.) are violated.
In general, these few rules keep our code pretty clean. They do not include discrepancies in the style of commenting or the overall architecture, but this team should compose for itself through the documentation. This is something where everyone can correct and supplement something.
Two CSS files
GitHub has a set of two CSS files,
github and
github2 . The file was split a few years ago to solve the problem of a
limit of 4095 selectors per file . This limit applies to versions of IE 9 and below, since GitHub requires support for IE9, our approach to sharing CSS will remain in place for a long time. Because today GitHub has about 7,000 selectors in these two files. Compare these figures with data from other sites:
- Bootstrap v3.2.0 has a little less than 1900 selectors;
- Twitter has just under 8,900 selectors;
- NY Times - about 2,100 selectors;
- SoundCloud (new version) - about 1100.
These numbers were generated by
cssstats.com . This is a very cool little utility that views your styles from the side that most developers, including me, do not usually look at their files. Also inside GitHub we use charts and usually use this for our own needs.
Inclusions via Sprockets
GitHub's CSS and JavaScript are enabled via Sprockets and
require . We develop our CSS code using separate subdirectories inside the
app / assets / shylesheet s
folder . Here's what it looks like:
/* = require primer/basecoat/normalize = require primer/basecoat/base = require primer/basecoat/forms = require primer/basecoat/type = require primer/basecoat/utility = require_directory ./shared = require_directory ./_plugins = require_directory ./graphs = require primer-user-content/components/markdown = require primer-user-content/components/syntax-pygments = require primer/components/buttons = require primer/components/navigation = require primer/components/behavior = require primer/components/alerts = require primer/components/tooltips = require primer/components/counter = require primer-select-menu = require octicons = require_directory . */
We include our dependencies (Primer is our internal framework) and after that we include all SCSS folder files in the order in which Sprockets decides to include them (it seems to me, in alphabetical order). The ease with which we can associate our styles (simply with the
require_directory command.) Is striking, but there are also disadvantages to this.
The order in which the styles are applied (and, accordingly, the order in which the files are included) is
important . In fact, this should not be the case, but in every design system there are rules and a basic style hierarchy. With the Sprockets approach, we sometimes run into specific issues. This happens because new files can be added to any of the two CSS files at any time. Depending on the file name, they appear in different places in the compiled CSS.
In addition, the use of Sprockets implies that your SCSS files do not have direct and automatic access to your global variables and mixins. This leads to the fact that you must include them (via
@ import ) every time at the top of each SCSS file that accesses a variable or mixin.
Performance
Inside GitHub we use a lot of graphs to track how the site and API are doing. We also track some interesting front-end features. For example, here is a
graph illustrating the size of our two CSS files over the past 3 months:
Also, here is a
graph of the number of selectors in CSS files for the last 3 months on our blob pages . Obviously, we still have a lot of work to reduce the number of tag selectors.
Due to the fact that we constantly add updated CSS files and add dozens of them every day, we constantly knock down the caches of our fairly large CSS files. We have done little to optimize file sizes or reduce cache failures, but we are starting to look more closely in this direction. It would be very cool to have a core file that, hopefully, will change very rarely and a secondary file that we would change more often.
Opening this topic on Twitter, we had (not sure if they have it now) 2 files,
core and
more .
The core file contained the styles necessary to ensure that the time elapsed before the first tweet was as small as possible. Everything else was in the file
more . Knowing the love of GitHub for rapid change (nothing is ported here, if it was not ported quickly), we will turn our attention to this approach. Now our two files are arbitrarily separated.
In general, optimizing the performance of selectors doesn’t bother us much . We are aware of bad approaches: excessive investments, ID, elements, etc., but we do not try to re-optimize. The only exception was the
diff page . Because of the too much markup that was needed to render the diff pages, we avoided attribute selectors like
[class ^ = ”octicon”] . When used too often, these attribute selectors can crash (and crash) browsers.
Documentation
Speaking about it, we are doing quite a good job, but we are also working on improvements. We have
CSS stylgide publicly available and all our general rules for writing CSS live there, as well as examples of most components. It is built on
KSS , a styled needle generator.
It is not perfect, but allows developers to find and use components quickly. It is also a great way to show new developers our development process, in order to quickly bring them to their usual development speed (like me when I joined about two years ago).
Primer
I hinted at this before, but for those who do not know, Primer p is our internal framework for common styles and components within our public and internal applications. It includes:
- Normalize;
- Global styles for box-sizing , typography, links, etc .;
- Navigation;
- Forms;
- Mesh;
- Markup styles;
- Special select menu.
We use it on GitHub.com, Gist and several internal applications.
Most of the components of Primer are documented in our stylgide (navigation, tooltips, etc.) Despite this, we recently started updating and improving Primer, so many components are now changing.
For those who wanted to ask, I would really like to put part of Primer in open access, but in this direction little will change in the near future. Still, I have hope.
Code refactor
We have a decent part of outdated code that includes CSS. Unlike a public project with open source, which has strict versioning rules, we get rid of unnecessary code regularly if these solutions suit us. Finding things to remove is done in two ways:
- Manual search for things that look similar, but actually have different HTML and CSS code, and the subsequent combination;
- Run a script that looks for a class in our CSS and checks if it is in our view files.
The overall refactoring process is probably not unique to GitHub. We find things that would be worth removing, delete them, publicly discuss, report this to the CSS team and port them as quickly as possible. Any team member can delete the code. We have a lot of developers who directly add something new to GitHub, but we also have a lot of tags that analyze what we can remove.