Medium Css Style Guide
medium-css-style-guide
User Manual:
Open the PDF directly: View PDF
.
Page Count: 29
| Download | |
| Open PDF In Browser | View PDF |
fat Follow
computer loser / captioned co-founder (captioned.com)
Aug 28, 2014 · 9 min read
Medium’s CSS is actually
pretty ***ing good.
In this episode i read my article
"Medium's CSS is actually pretty…
Listen to the audio version above?
I always believe that to be the best, you have to smell like the best, dress like
the best, act like the best. When you throw your trash in the garbage can, it
has to be better than anybody else who ever threw trash in the garbage can.
— Lil Wayne
I’ve been meaning to write something about the CSS at Medium for a
while because I’m not completely ashamed of it…
So how did that happen? What did we do di erently? OMG, how can
you do the same thing? Or learn from us, or something?
What follows are some notes on our CSS, the steps we’ve taken to get it
to where it is, and the world we live in today.
The best at drawing pictures of myself
. . .
In the beginning (some history)
Roughly 2 years ago I joined Obvious Corp. to work on the software
application, medium.com (which, you’re ~hopefully~ reading on).
At the time, Medium had already gone through a series of “re-styles” (A
re-style is where designers ruin your life by coming up with something
prettier than they had previously come up with, resulting in you having to
rewrite a bunch of CSS/LESS/SASS/etc). Because of these re-styles,
there was a lot of built up cruft – the company was leaning pretty
heavily on LESS’s language features, with page driven semantics, nonsprited/non-retina image assets, and other such things.
I dug through git and managed to pull out our old pro le page
implementation from back in 2012 ~shudder~. Check it out below and
notice some of these unfortunate patterns:
•
everything nested under a .pro le-page class, with zero reusable
components (also ~almost~ everything is pre xed with .pro le)
•
super generic variable names like @link-color which weren’t
scoped to anything, but presumably only to be used in pro lepage.less
•
deep nesting (.pro le-page .pro le-posts .home-postsummary .home-post-collection-image img is an actual selector
being generated below — super rough on perf)
•
no image spriting
•
no z-index scale, no font scale, no color scale, no scalesss…
1
// Copyright 2012 The Obvious Corporation.
2
3
@column‐margin: 9px;
4
@link‐color: rgb(78, 78, 78);
5
@link‐hover‐color: rgb(160, 160, 160);
6
7
.profile‐page {
8
margin: 0 0 0 55px;
9
width: auto;
10
font‐family: helvetica, arial, sans‐serif;
11
line‐height: 1.2em;
12
13
.profile‐left {
14
width: 925px;
15
float: left;
16
17
h1 {
18
height: 55px;
19
line‐height: 55px;
20
text‐transform: uppercase;
21
font‐size: 15px;
22
font‐weight: 700;
23
padding‐left: 30px;
24
}
25
26
.profile‐details {
27
background‐color: rgb(47, 47, 47);
28
padding: 47px 37px;
29
position: relative;
30
31
.profile‐settings‐link {
32
position: absolute;
33
width: 48px;
34
height: 48px;
35
right: 37px;
36
top: 22px;
37
text‐indent: 1000px;
38
overflow: hidden;
39
background: url(/img/placeholder‐gray.png) no‐repe
40
}
41
42
fil
i
{
42
.profile‐image {
43
float: left;
44
margin‐right: 47px;
45
46
img {
47
width: 128px;
48
height: 128px;
49
.border‐radius(64px);
50
}
51
}
52
53
.profile‐name {
54
font‐family: georgia, "Times New Roman", serif;
55
font‐size: 32px;
56
font‐style: italic;
57
font‐weight: 400;
58
color: white;
59
text‐shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
60
text‐transform: capitalize;
61
}
62
63
.profile‐bio {
64
margin‐left: 175px; // = 128 + 47
65
font‐size: 16px;
66
line‐height: 1.2em;
67
font‐weight: 400;
68
color: rgb(224, 224, 224);
69
70
}
}
71
72
.profile‐stats {
73
.clearfix;
74
height: 66px;
75
line‐height: 66px;
76
padding‐left: 28px;
77
border: 1px solid rgb(211, 211, 211);
78
border‐top: none;
79
background: rgb(245, 245, 245);
80
81
.profile‐stats‐label {
82
float: left;
83
height: 66px;
84
line‐height: 66px;
85
text‐transform: uppercase;
86
font‐size: 15px;
87
font‐weight: 700;
88
color: rgb(77, 77, 77);
89
}
90
91
.profile‐stats‐item {
92
width: 139px;
93
height: 66px;
94
line‐height: 66px;
95
border‐left: 1px solid rgb(211, 211, 211);
96
float: right;
97
text‐align: center;
98
font‐family: georgia, "Times New Roman", serif;
99
font‐size: 14px;
100
font‐weight: 700;
101
color: rgb(153, 153, 153);
102
background: transparent url(/img/placeholder‐gray.
103
padding‐top: 16px;
104
.box‐sizing(border‐box);
105
106
}
}
107
108
.profile‐posts {
109
border: 1px solid rgb(211, 211, 211);
110
border‐top: none;
111
background: white;
https://gist.github.com/fat/a4af78882d0003d2345e
. . .
The 1st Project: OMG Images
At the time, I had been working a lot on library code (Bootstrap,
Ratchet, etc.) and sweating all the details, trying to write the best CSS
humanly possible.
Medium’s CSS was clearly di erent. Not a neutral di erence, but a soul
crushing, shitty di erence. And I wanted to x that.
Looking at all there was to do, the rst thing I tackled was images. I
remember being pretty appalled that we weren’t doing any sort of
spriting in 2012… accumulating hundreds of image assets like
placeholders, arrows, icons, etc. in this crazy /img/ directory which
was something like a graveyard for icons.
To get away from that world I did two things — rst, I wrote a little CSS
utility script called SUS (which we’re still using today and I’ve just open
sourced here: https://github.com/medium/sus). This was largely
written in IRC with the help of Guillermo Rauch, and is used to extract
images from stylesheets and lazy load them in a separate, data-uri le.
Not an original concept, just something simple that could be done to
help us get to where we needed to go.
The second thing was Geo
Teehan and I sat down and
created Medium’s rst icon
font. At the time, we largely
had no idea what we were
doing… but with the help of
icomoon, a github blog post,
and a few long nights, we were
able to ship something
~pretty~ good to Medium.
This was huge because it meant we could delete like 97% of the img
folder, everything looked great on my retina MacBook Pro, and we
were requesting signi cantly fewer resources.
. . .
The 2nd Project: Scales
The next major project for me was the z-index scale. Z-index is one of
those things which can get out of hand super easily and has always
driven me crazy. I didn’t want this to be the same at Medium.
Before this project began, it was very common to see one element with
a z-index: 5, next to a sibling with a z-index: 1000000; and another
sibling z-index: 1000001; and another z-index: 99999;.
The styles for these were spread all across the code base and there was
no clear way of guring out how to sort these.
So I took on the laborious task of manually auditing the entire
codebase for z-index values. I then introduced a private scale (private
to z-index.less) which could be used for z-indexing components
(limiting it to 1-10). And nally, I moved all the actual assignments of
this scale internal to z-index.less, so you could see how di erent
components were situated relative to each other (which is actually
pretty handy).
Below is the z-index le we are using on medium.com today.
1
// Copyright 2014 A Medium Corporation
2
//
3
// z‐index.less
4
// Medium.com's z‐index scale. Z‐index values should always
5
// allows us to at a glance determine relative layers of ou
6
// arrising from arbitrary z‐index values. Do not edit the
7
// scoped z‐index values.
8
9
10
// Z‐Index Scale (private vars)
11
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
12
@zIndex‐1:
100;
13
@zIndex‐2:
200;
14
@zIndex‐3:
300;
15
@zIndex‐4:
400;
16
@zIndex‐5:
500;
17
@zIndex‐6:
600;
18
@zIndex‐7:
700;
19
@zIndex‐8:
800;
20
@zIndex‐9:
900;
21
@zIndex‐10: 1000;
22
23
24
// Z‐Index Applications
25
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
26
@zIndex‐1‐‐screenForeground:
@zIndex‐1;
27
@zIndex‐1‐‐followUpVisibility:
@zIndex‐1;
28
@zIndex‐1‐‐prlWelcome:
@zIndex‐1;
29
@zIndex‐1‐‐appImageDropdown:
@zIndex‐1;
30
@zIndex‐1‐‐surfaceUnder:
@zIndex‐1;
31
@zIndex‐1‐‐blockGroup:
@zIndex‐1;
33
@zIndex‐2‐‐surfaceOver:
@zIndex‐2;
34
@zIndex‐2‐‐imagePickerControl:
@zIndex‐2;
35
@zIndex‐2‐‐collectionCardButton:
@zIndex‐2;
36
@zIndex‐2‐‐blockGroupButtonGroup:
@zIndex‐2;
37
@zIndex‐2‐‐blockGroupFocused:
@zIndex‐2;
38
@zIndex‐2‐‐blockGroupOverlay:
@zIndex‐2;
40
@zIndex‐3‐‐caption:
@zIndex‐3;
41
@zIndex‐3‐‐blockInsertControl:
@zIndex‐3;
32
39
42
42
43
@zIndex‐5‐‐figureOverlay:
@zIndex‐5;
44
@zIndex‐5‐‐highlightMenu:
@zIndex‐5;
45
@zIndex‐5‐‐metabar:
@zIndex‐5;
46
@zIndex‐5‐‐profileAvatar:
@zIndex‐5;
47
@zIndex‐5‐‐noteRecommendations:
@zIndex‐5;
48
@zIndex‐5‐‐collectionLogo:
@zIndex‐5;
49
https://gist.github.com/fat/1f6da6b3bd0311a1f8a0
Of course, this z-index scale was quickly followed by a color scale
(variables for blacks, grays, whites, brand-colors, etc) and a type scale
(variables for font-sizes, weights, letter-spacing, line-heights, etc.).
Also, you might notice the variable names have taken on some stricter,
semantic value – more on that later…
. . .
The 3rd Project: New Style Guide
Not long after introducing scales to Medium, we started in on a big restyle code named “Cocoon”.
Cocoon deprecated several post templates (Medium originally had
image templates and quote templates, not just a single article template)
and also introduced post lists instead of post “cards.”
We took this as a chance to
step back and write a new
style guide for Medium.
This initial e ort was shared
between myself, Dave
Gamache, Dustin Senos, and
the talented Chris Erwin.
We took some time to really
tighten up our thinking
around writing production
CSS/LESS at Medium — the major updates being:
•
Limit LESS to variables and mixins (no nesting, guards, extend,
etc.) — while these language features ~can~ be powerful, we
found that inexperienced LESS developers often got in trouble
with them fairly easily. We also just preferred the visual aesthetic
of pure CSS (and the consistency it a orded) and wanted to move
our codebase as far in that direction as reasonable.
•
Classes and IDs are lowercase with words separated by a dash —
this is how most of us had been writing CSS at the time with
Bootstrap, Skeleton, Ratchet, etc. And we thought it made sense to
do the same here. Another reason is it followed the naming
conventions set forth by the CSS language itself, i.e. border-radiustop-left, etc.
•
Prefer components over page level styles — we wanted our front
end to feel like library code, and began to break les like
pro le.less into smaller more focused les like button.less,
dialog.less, tooltip.less, etc.
Here it is in its entirety:
.user‐profile {}
.post‐header {}
#top‐navigation {}
.userProfile {}
.postheader {}
#top_navigation {}
icon‐home.png
iconHome.png
icon_home.png
iconhome.png
icon‐home.png
bg‐container.jpg
bg‐home.jpg
sprite‐top‐navigation.png
home‐icon.png
container‐background.jpg
bg.jpg
top‐navigation.png
rgb(50, 50, 50);
rgba(50, 50, 50, 0.2);
#FFF;
#FFFFFF;
white;
hsl(120, 100%, 50%);
hsla(120, 100%, 50%, 1);
@z‐index‐1 ‐ @z‐index‐9
kill ‐9
font‐weight
bold
font‐weight
.wf‐sans‐i7, .wf‐sans‐n7, etc.
n
i
4
7
=
=
=
=
normal
italic
normal font‐weight
bold font‐weight
ex:
@type‐micro
@type‐smallest
@type smallest
@type‐smaller
@type‐small
@type‐base
@type‐large
@type‐larger
@type‐largest
@type‐jumbo
.homepage‐nav
.homepage‐nav
.nav
.profile‐header
.nav,
h
.nav‐bar
.header‐hero‐unit
.home‐nav,
.profile‐nav,
.nav,
.nav‐bar,
.nav‐list
.home‐page {
.nav {
margin‐top: 10px;
}
}
.list‐btn {
.list‐btn‐inner {
.btn {
background: red;
}
.btn:hover {
.opacity(.4);
}
}
}
.list‐btn .btn‐inner {
background: red;
}
.list‐btn .btn‐inner:hover {
.opacity(.4);
}
.content,
.content‐edit {
…
}
.content, .content‐edit {
…
}
.content {
…
}
.content‐edit {
https://gist.github.com/fat/b27700946c777adacdf4
It wasn’t perfect by any means, but it cleaned up a few basic ideas and
got us headed towards what felt like the right direction.
Unfortunately, even after this style update, people were still struggling
with when to make a component, when to make a subcomponent, etc…
And we were getting the occasional unfocused, run-on classname
like: .nav-on-light-background-button or .button-primary-sidebarover-blur.
People weren’t pre xing classes at a page level anymore (which is
great), but instead they were stringing together really long lists of
arbitrary words separated by dashes. The evolution being: .button
→ .button-primary → .button-primary-dark → .button-primarydark-container → .button-primary-dark-container-label, ad
nauseam.
. . .
The 4th Project: Identifying the Future
About this time I started writing ~a lot~ about CSS internally at
Medium with the lofty goal of it becoming the best in the world. I didn’t
really know what that meant, but I knew our current direction just
wasn’t working.
People were confused. And what’s worse, they were
thinking they were writing CSS really well, when in
reality they weren’t.
So, I started looking around — poking at frameworks, trying di erent
tools, philosophies, talking to friends, talking to friends of friends, etc. I
soon identi ed three major projects that I wanted to tackle in order to
get our CSS to where I thought it needed to be.
1. Introduce new CSS variable, mixin, and classname semantics to
avoid run-on classnames and improve readability
2. Move us o of LESS and onto Rework for more powerful mixin
support and a syntax even closer to vanilla CSS
3. Add tooling around CSS performance (load time, fps, layout time,
etc) — to make it easier to track style changes and regressions over
time
. . .
The 5th Project: Semantics
I wanted stricter semantics for our code base because for a team of our
size I felt like it would be easier to have rules for us to lean on. And I’d
rather have something a bit more complicated if that also meant people
had to be more mindful when creating new CSS classes. At all costs, I
wanted to avoid the run-on classname, or at least make it harder to
create.
I began talking at length about it with Daryl Koopersmith and my good
friend Nicolas Gallagher.
Nicolas and I have an
interesting relationship in that
Nicolas always tells me
something, I say he’s wrong,
call him names in french (Va
te faire foutre, enculé), and
dance around for a few weeks,
before inevitably realizing he’s
right, and taking his idea as
my own.
This time was no di erent, and after a few late nights, I was eventually
convinced of a style similar to the one Nicolas outlined in SUITCSS.
Similar, but ~slightly~ better.
So I began by plagiarizing the hell out of Nicolas. I threw away our old
style guide and copy pasted large portions of his, editing a few things
here and there.
Eventually I came up with our current guide, which you can read in its
entirety below, the main additions being:
•
.js- pre xed class names for elements being relied upon for
javascript selectors
•
.u- pre xed class name for single purpose utility classes like .uunderline, .u-capitalize, etc.
•
Introduction of meaningful hypens and camelCase — to
underscore the separation between component, descendant
components, and modi ers
•
.is- pre xed classes for stateful classes (often toggled by js) like .isdisabled
•
New CSS variable semantics: [property]-[value]--[component]
•
Mixins reduced to poly lls only and pre xed with .m-
js‐
js‐
{$text}
p
{$
} /p
u‐
u
[‐‐modifierName|‐descendantName]
.myComponent { /* … */ }
…
/* Core button */
.btn { /* … */ }
/* Default button style */
.btn‐‐default { /* … */ }
…
…
is‐stateName
.tweet { /* … */ }
.tweet.is‐expanded { /* … */ }
…
‐[‐‐componentName]
@color‐grayLight‐‐highlightMenu: rgb(51, 51, 50);
rgb(50, 50, 50);
rgba(50, 50, 50, 0.2);
#FFF;
#FFFFFF;
white;
hsl(120, 100%, 50%);
hsla(120, 100%, 50%, 1);
@zIndex‐1 ‐ @zIndex‐9
https://gist.github.com/fat/a47b882eb5f84293c4ed
It’s one thing to copy paste a style guide, it’s another to rewrite all of
your CSS application.
Thankfully I was able to convince the company of the importance of
this project, if only for my sanity, and was given 2½ weeks to rewrite all
the CSS on Medium.com — which if you’re following along, is a fraction
of the amount of time it took for us to x CSS underlines.
That said, this rewrite was not only cathartic, it cleaned up loads of
dead styles, tightened up and generalized implementations across the
site, broke les out into smaller subcomponents, and to my surprise,
only caused a handful of rollbacks.
Of course, refactoring CSS meant also refactoring our templates —
adding more encapsulation and stricter semantics across the board.
Now today, rather than having hundreds of one o
tags with
random avatar classes for example, we have a single centralized avatar
template, which accepts boolean options to trigger di erent modi ers
like size, style, etc. This makes updating styles easier and
implementation detail bugs much less frequent.
. . .
X Project: The future?
Undoubtedly we are in a better place then we were 2 years ago. Writing
CSS at Medium is actually pretty pleasant and devs with di erent
experience levels are contributing some pretty great stu .
That said, the next CSS project will have to be around performance. As
we continue to grow our story pages and push them to the next level,
you can imagine how getting accurate, reliable measuring tools around
layout and rendering performance is incredibly important… and kinda
just sad that we don’t already have in 2014.
So that’s it. There’s more to do, but I’m feeling pretty good about it all
at the moment. Which again, is better than usual I think.
Cheers if you made it to the bottom of this long boring post.
Thanks so much to Katie, Dave, Mark, Koop, Kristofer, Nicolas, and
others for helping me write this thing (and x Medium) without even
probably realizing it
PS
(If you enjoy this?! Consider reading my take on React and Redux:
Isn’t our code just the *BEST*
Views from the 6 weeks in hell I spent rewriting
bumpers in react.
medium.com
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf Linearized : No Page Count : 29 PDF Version : 1.4 Title : Medium’s CSS is actually pretty f***ing good. – fat – Medium Producer : Mac OS X 10.12.6 Quartz PDFContext Creator : Chrome Create Date : 2018:03:09 16:45:29Z Modify Date : 2018:03:09 16:45:29ZEXIF Metadata provided by EXIF.tools