With ECSS you write a unique scoped class for each and every item.
- Aim to be explicit in the code's meaning
<div>
s are no good. Add a class- If it's unclear, add good comments
- There's nothing wrong with adding a class if the element doesn't describe the situation properly, but aim to use raw html as much as possible.
gl-Form p
isn't as good as.gl-Form .error
ofgl-Form_Error
- Should it be scoped? (to global or page)
- GPS aims to reduce bloat that BEM and ECSS bring.
- Simplifying class names, using raw html elements to style, reducing repetition of code
- Aim to be consistant with your naming conventions
#fragments
for links can be safely ignored- Similar is not the same, have a solution
- I don't think
.gl-Class { #page & { ... } }
is a good idea. - It adds complexity where there should be reliable continuity in design
- When there's more than a few lines of code is different, consider splitting it into a new
.gl
element (rather than a variant. If more than that, make it a unique#section
.
- I don't think
- You'll need to refactor if you move elements around
- If you wrap your
p.error
in<div>
what then? - Do you disallow
>
specific css?
- If you wrap your
- Always properly scope css to avoid inheritance issues
- Never wrap or give a class if it isn't being used?
- Flat style css is preferred, but 3 levels deep is ok (nested)
- Styles that are nested must have demos available (so they're easy to reason about)
- Better to declare overrides within the child class, such as
.nightMode
or@media screen
but these should be few and far between. The latter might need to be used often - Use Tailwind's method of reusing styles wherever possible.
#page
and#section
elements should be unique. Usegl-Demo
orgl-GridHeader
etc if you're reusing styles? Or just repeat yourself?- NEVER style a
.gl-
element with a#page
or#section
. Always use a-variant
class on the.gl-
parent, or create a unique#section
. TL;DRgl
obal means it's the same site wide, full stop.
HTML5 tags may still not be enough to guide you in what each element is for. Create a design-system, or make it obvious with comments or classes. Aim to isolate styles. Make rules concrete.
If your h1
element is always wrapped in a gl-Card
just style the h1 font and margins that way? If it's not going to be visible outside this context.
A good example is code
in Anki flashcards. It needs font-size: inherit
and this kind of style should live in one place for all it's variations. Basically, inherit it's parent styles.
GPS doesn't solve every problem. I'm sure there's more that ECSS is solving that we'll have to consider at some point.
GPS seems to be sometimes a bit messy. Personal opinion. Subjective.
With GPS it seems fuzzy when to use
.gl
styles or#id
in some situations. Or even which should be raw html styles and which needs a class or id. Code order becomes more of a challenge too, for instance I'm needing thecode
to inherit styles fromh1
, but I want to name styles ofcode
altogether for ease of scanning. If my code styles (inherit) come after myh1
styles, will they inherit that header style? Probably not!.Sometimes it's best to be explicit and style them insitu (i.e. the
.gl-Card
that they live in, rather than at the root raw html level)
code {
.gl-Card header h1 &,
.gl-Card header h2 &,
.simple-Header_Code & {
b, strong,
i, em {
font-size: inherit;
font-style: inherit;
font-weight: inherit;
text-decoration: none;
background: none;
}
I'm not sure if the above is correct, or if it should always live with /partials
raw html. I'm also not super keen on the nested style over the .gl-Card_HeaderTitle
style. GPS is all about gettin out of the BEM habit however.
It can take a long time to switch between frameworks (about 2-3 days with Anki), so better to choose wisely and stick with it.
- Is GPS suited to all types of sites?
- How do we tackle fringe or subjective problems?
- How does it cope at scale, with a team of devs?
Repetition should be avoided (see sharing styles) so here's your options:
- A loop
- A component
- Some kind of conditional css (depending on type of card)
In some ways it's nice to go back to styling raw html elements (which can be wrapped in a .gl-
class) but I don't personally like overly nested elements. With ECSS you could scan the css file and see very quickly what was part of what:
- The file lived with it's page or component
- It has the concept of
.gl-
styles (which in my portfolio is for true global styles, such as.gl-Nav
,.gl-Header
, which are site-wide ... - Things that affect portions of the website, such as a
/page
template, would be scoped as.pg-Intro
, and it's child elements `.- These live in a folder named
/pages
- Page templates live here also.
- These live in a folder named
- Next we have page-level sections
/pages/manifesto
, which may be exactly the same as other page sections, but we scope them as unique individual pages;.manifesto
,.about
, etc. - Elements that live inside the
/manifesto
template, even though they're.gl-
elements as far as GPS are concerned get scoped to the page. .manifesto-List
and.manifesto-Photos_WithChildren
or.manifesto-Button-variant
are all examples of this line of thought. So there's a lot of repetition across the site.
There's some subtly to how we decide things that are .gl-
elements in ECSS, but less so than GPS it seems to me. If there's likely to be differences, ECSS says treat them as unique components. Our Anki cards in that case scope to .simple-Card
and .missing-Card
with all their children. They share nothing.
How can we reason about all our elements with ease?
With ECSS you didn't have to wonder what an element was or where it was positioned. With GPS the naming conditions are slightly different:
- Is
.gl-header
part of.gl-card
or it's own thing? Can I place.gl-header
anywhere or is it dependant on it's relative position? - When do I add a class? A
p
tag is a bit weak and doesn't give any semantic information, for instance. - If we move elements around, this must be reflected in our CSS, unless that element is it's own thing.
What happens when a global component mostly shares styles, but has some minor differences. At what point do we split into a new
.gl-
component, or a-variant
? When do we create a unique#section
?
A good for instance here is Missing!
and Simple
cards are almost identical, but #answer
wraps differently.
If we wanted to create a Draw!
card type which uses images, our .gl-CodeBlock
isn't required anymore (which I named as a separate entity for this reason). How far can we stretch our design system before we have to be unique?
Again, ECSS didn't have this issue as almost EVERYTHING was unique.
- GPS has some nuances that ECSS does not suffer from.
- Folder names and file names should make sense with our new framework
- Raw html should be separate from
.gl-
styles and#page #section
s
- Raw html should be separate from
- The
/demo/
folder is equivalent to a font specimen or design system.- We should have a base
specimen
file, for defaults styles, as well as a design system for our.gl-
styles. - These should be consistant and the place you go preview any changes.
- We should have a base
- Make sure there's no
.gl
styles that should be raw/partials
html and vice-versa.- As much as is possible should be styled in raw html (base styles)
- Styles such as
.gl-Card h1 code b
might best live in/partials
with our specimen styles. Do we always seth1 code
asfont-size: inherit;
for instance? #section
and.gl
have to be carefully considered. A#section
should be unique. It gets complicated when you have slight differences between two components. You can move it up or down the scope.- A
#section
should always be underneath a#page
(I think)
- Our
.anki-Front
and.anki-Reverse
are now#front
and#reverse
. They're sort of pages (as they're complete Anki templates) but within#demo
they're more like sections. It's a view within a view. What to do here?- Our
#demo > ...
classes are pretty much just grid components to give ourselves some page layout. Do we name them as.gl-Grid
components or use a page id? - ECSS would treat every class as unique, and scoped to it's page/component.
- Our
Does aIt seems a#section
have to be a unique element, only one per site, nested inside a unique#page
?#section
should be unique to, and nested inside, a#page
level element.- A piece of styling that applies to ONE area only should be under at least
#page #section
ids (2 levels). - If you had
#photo
and insidegl-Photo
should the.gl-
element be left alone? Is it wrong to style it dependant on the#photo
section? (such as.gl-Photo { #photo & { .. } }
) - What about our "uniqueness" problem, where two pages are similar, but not the same?
- If we have a slight
-variant
element, do we treat this as a brand new thing?
- A piece of styling that applies to ONE area only should be under at least
- If an element is only seen in one type of card, such as
#missing > .cloze
I'm guessing it should be a uniqe#section
or.class
? (in our case, the.cloze
class is required by Anki).- i.e: where should it live?
.gl-
elements should only be nested if they contain multiple children, or for-hover
and-active
states.
Do we give our "views" in Anki a unique
#page
ID? How do we account for variations in our.gl-Card
which both#missing
and#simple
will share? How do we name the.gl-Card
-front
and-reverse
?
<!-- The simple card had the `.simple-Sample` on
front and the `.simple-KeyPoint` and `.simple-KeyPoint_Code` on reverse. -->
<div class="simple-Sample">
{{★ Sample (code block)}}
</div>
<!-- The Missing! card only has the `.missing-KeyPoint` on the front and reverse. Both-->
<div class="missing-KeyPoint"> <!-- There's no CSS for this at all -->
<div class="missing-KeyPoint_Code"> <!-- Same CSS as `.simple-Sample` -->
{{cloze:★ Key point (code block)}}
</div>
</div>
We also used to use {{FrontSide}}
tag for our #simple
.gl-Card
because it's simpler to do that and render our .gl-Card
-front
within our -reverse
template. This won't work if we want to share styles with Missing!
Not only do we have the above issue, where they're using different {{card fields}}
but our Missing! card wraps the entire -reverse
in an div#answer
(our Simple template uses #answer
as a child of -reverse
).
Here in lies the conundrum.
<!-- Missing! -->
<div id="reverse">
<div id="answer">
<!-- Missing! wraps everything in an `#answer`
so we can skip to the right place on reveal -->
</div>
</div>
<!-- Simple -->
<div id="reverse">
<section class="gl-Card">
<!-- We used to use `{{FrontSide}}` here, but we can't do that and share our templates with Missing! -->
<div id="answer">
<!-- Simple has it's skip to link further down the page -->
</div>
</section>
</div>
Luckily we don't actually style #answer
as it's just an internal link, but it begs the question "what if we want to share a template but it's layout forbids it?". Do we ...
- Create a new almost identical element (and assume it must be a
#section
as it's used in one place); - Create a
.gl-
-variant
for this one scenario, bearing in mind we may only use it once; - Create some
.gl-
bits that are universal, and#page #section
or#page .class
for the bits that are unique? - Some other hybrid solution?
What GPS is trying to achieve.
Full article and Github repo.
GPS stands for global
, page
, section
. If you're in a team, you need a document to work from, so you should definitely have a design system at least for your global
styles. ECSS was all about complete isolation, but GPS isn't so strict. It's trying to achieve:
- Cleaner code (dare I say, beautiful code?)
- Fewer unwieldy class names
- Repetition (BEM and ECSS aren't dry; neither is Tailwind!)
- Just enough isolation (to stop inheritance issues, and what he calls "scope leaks")1
- The simplicity of frameworks like Pico CSS.
For this I'm still using
ECSS
naming convention ofglobal-CapitalCase
. Elm lang uses CamelCase too.
Some styles are used in multiple places across a website. For example, your h1
element might consistently look the same (and it should!), or you might have a .box
class that wraps a piece of content in an elegant box, or a .button
that creates a standard button on your site. These are good candidates for global styles. Global styles are great, they give a sense of unity to your site, and should be used frequently. If you’ve seen something like a brand manual or style guide, everything within represents global styles.
Should always be classes, and styles should only be defined as global if and only if they appear on multiple different pages.
You should probably have a unique #id
for a page or view element, and all page-level styles (that are not global elements) should live here. Any CSS that you write that is not global should always be scoped under a page or view’s id.
Ids are intended to be used for elements that only appear once per page — it is invalid html to have more than one of the same id on a page. I think it's also invalid to have more than one #id
on the same element?
A unique section within a page
If you are writing styling that applies only to one specific section, it should be nested under at least 2 levels of ids — the page id and the section id. This is the default place you should put code if you aren’t sure, as there is no chance of a scope leak for code here.
Pull it up to page-specific style: if later on you notice that the same style is actually used elsewhere on the same page
Pull it up to global: if you notice it being used on other pages!
If you are copy-pasting blocks of CSS in order to get around this problem you are doing it wrong.
A non-reusable section or element that is page-specific, and isn't going to be reused across the site. For example, on your about page, you might have a portion that contains an introductory paragraph, then a section that has some of your company’s staff, and a section that has some of the clients you’ve worked with.
Since there can only be one of them per page, these are also marked with ids.
Footnotes
-
By far the largest issue developers have with CSS are what I like to call scope leaks. These happen when you write styles for one specific section of a website, but because of the way you made the selection, the styles also affect elements on other random parts of the site. This is a side effect of CSS being written in the global scope by default. ↩