The CSS Selection

The state of real-world CSS usage, 2026 edition.

User avatar for Bart Veneman Bart Veneman

Welcome to The CSS Selection 2026! In this article we’re having a look at how CSS is used at scale in over 100,000 websites. We’ll look at what things are common in most websites and discover interesting outliers.

This article exists for several reasons but the Web Almanac is the most prominent one. For several years the Web Almanac has skipped the CSS chapter: a shortage of authors, editors but mostly analysts who could mangle BigQuery stuff to get hold of large amounts of CSS data to analyze and put them in readable charts. Additionally the Web Almanac uses a regex-based CSS analyzer that differs a lot from Project Wallace’s analyzer. This has bothered me for years because I think the CSS community deserves a yearly overview as well as that overview having the best in class analysis. Now, I can’t crawl millions of websites like the HTTP Archive can, but I can do 100,000 and still make a pretty decent overview. So, that’s what we’re looking at now.

CSS has taken a flight in recent years witch many new features, properties, values, atrules and so much more. With power shifting to CSS it’s interesting to look at the use of these new features as well as keeping an eye on global metrics like file size, units used etc. We’ll work our way down from the top so we’ll start by looking at global metrics followed by atrule analysis, then rules, selectors, declarations and lastly, values.

Stylesheet composition

To get a global idea of what we’re looking at it’s good to take a look at some numbers from a bird’s eye view. What is our CSS made up of?

CSS Size

How much CSS does a website ship to their users? For this we simply look at how large the string of CSS is.

Stylesheet size distribution 0 B200 kB400 kB600 kB800 kB1 MBp10p25p50p75p9032.6 kB130 kB309 kB619 kB1.12 MB

Lines of code

Because most (?) websites minify their CSS it’s also relevant to look at the lines of code. In more traditional programming languages this is a common metric to get a sense of the magnitude of a program. For CSS I’ve determined that:

source lines of code = # of rules + # of atrules + # of declarations

With that established, let’s look at the numbers:

Stylesheet Source Lines of Code distribution05,00010,00015,00020,00025,00030,000p10p25p50p75p901,0294,55110,67720,01333,457

It looks like most websites manage to stay below or around 10,000 lines of code. That’s still a lot of CSS but looking at any regulary-sized website tells you that declaring your design system with a bunch of custom properties and loading some third-party tools quickly ramps up the numbers.

Further analysis of atrules/rules/selectors/declarations per page is in their respective chapters.

Stylesheet complexity

One of my favorite metrics is complexity. Using it besides the Source Lines of Code and file size gives a quick impression, even more when comparing the number between websites. Or different versions of the same website, like a staging environment and a production environment. This chart shows the total complexity of all the CSS on a page including that for complex selectors, property browserhacks, use of vendor prefixes and much more.

Stylesheet Source Lines of Code distribution020,00040,00060,00080,000100,000p10p25p50p75p902,97813,74333,18865,140114,893

The distribution across percentiles here is pretty much the same as for lines of code.

Embedded content

One contributer to file size is embedded content. A good portion of websites embed various types on content in their CSS, like encoded images or sometimes even entire WOFF2 files.

Embedded content sizes per percentile 0 B5 kB10 kB15 kB20 kB25 kB30 kB35 kBp10p25p50p75p90 0 B 0 B2.23 kB8.23 kB36.6 kB

This is an interesting distribution because we can see both the 10th and 25th percentile don’t embed any content at all. Only from the 50th perentile onwards we see a little bit of embedding, with the chart steeply rising from the 90th percentile.

Comments

Another contributer to file size can be the presence of comments. Minifiers usually strip most comments but some remain, like copyright notices.

Stylesheet comment sizes per percentile 0 B2 kB4 kB6 kB8 kB10 kBp10p25p50p75p90 0 B72 B729 B3.84 kB11.4 kB

It’s encouraging that, like the embedded content, the amount of comments in shipped CSS is pretty low.

Atrules

Totals

Let’s start by looking at how many atrules we’re shipping. Not looking at which ones specifically, just count how many there are in total.

Distribution of percentiles of total CSS atrules per website0100200300400500600700800p10p25p50p75p90858173394740

Looks like most websites have that atrules well under control. With modern CSS there’s a lot to control with atrules, like containers, media, keyframes, supports. You need a bunch of them to get a properly functioning website!

@media

Adoption rate: 93.06% Baseline since: July 2015

Our versatile friend the media query. Always there to help with that responsive design, making that page print-ready or adjust the layout for the folks who love that extra dash of contrast. So many possibilities!

Let’s start by looking at how many of them are on the page at all:

Distribution of percentiles of total CSS @media atrules per website0100200300400500600p10p25p50p75p90333116285577

Looking at the difference between this graph and the one above with atrule totals I think it’s safe to say that the largest amount of atrules on a page would be @media. And that would make a ton of sense if you look at the versatility of it. So, what do we use it for actually?

FeatureAdoption %Relative count
max-width88.29%
min-width86.45%
prefers-reduced-motion44.15%
orientation25.06%
hover23.79%
max-height23.23%
-webkit-min-device-pixel-ratio22.60%
min-resolution20.32%
-ms-high-contrast15.65%
max-device-width13.32%
forced-colors8.59%
pointer7.54%
min-device-pixel-ratio6.35%
-webkit-transform-3d6.07%
transform-3d5.94%
min--moz-device-pixel-ratio5.75%
min-device-width5.66%
min-height5.52%
prefers-color-scheme5.11%

The max-width and min-width features are quite unexpectedly used on most websites (88% and 86%). I did not expect prefers-reduced-motion to have such a great adoption rate even though it’s almost half of that of the top two. There are some other surpises here, like forced-colors being used a bunch more than prefers-color-scheme.

@font-face

Adoption rate: 85.61% Baseline since: July 2015

It’s hard to imagine these days that we had to embed images or use Flash to make our typograhpy look great. Luckily @font-face has made our lives a lot easier. How much do we use it?

Distribution of percentiles of total CSS @font-face atrules per website01020304050p10p25p50p75p900381948

Given the fact that you need multiple @font-face rules for every weight and italics it makes sense that the numbers here are like this. I had actually expected the numbers to be a bit higher. Maybe some of you are experiencing some faux-bolds? 😉

@keyframes

Adoption rate: 83.90% Baseline since: September 2015

Continuing the making-our-website-pretty theme with a look at @keyframes. Every website has at least one spinner nowadays, right?

Distribution of percentiles of total CSS @keyframes atrules per website020406080100p10p25p50p75p900292693

Okay, maybe not every website then. 10% of them are as static as can be!

@keyframes nameAdoption %Relative count
fa-spin25.68%
spin23.90%
progress-bar-stripes21.44%
fadeIn18.53%
pulse15.78%
fadeOut15.07%
swiper-preloader-spin14.51%
bounce11.28%
turn-off-visibility10.95%
lightbox-zoom-out10.94%
lightbox-zoom-in10.94%
turn-on-visibility10.94%
fadeInUp10.62%

It looks like @keyframes do make things go spin round. fa-spin is a clear sign of FontAwesome being present. The turn-off-visibilty seems to come from the WordPress Gutenber editor as well as the two lightbox-zoom-* keyframes.

@supports

Adoption rate: 44.57% Baseline since: September 2015

This atrule is still evolving into something better even though it has been in our toolbox for quite a while now (baseline since September 2015!). Where we first only could check if a certain declaration was supported we now also can check for the support of selectors, font-tech and font-format!

Distribution of percentiles of total CSS @keyframes atrules per website0246810p10p25p50p75p9000036

Uh-oh, not quite what I expected. Either everyone’s progressive enhancement game is top notch or we’re missing out big time on some quality of life improvements. I really wonder why the adoption rate of @supports is this low. An underrated tool, it seems.

@import

Adoption rate: 18.04% Baseline since: July 2015

If our previous atrule was used so little, let’s hope this one is too! Using @import usually is an anti-pattern unless you know what you’re doing.

Distribution of percentiles of total CSS @import atrules per website0246810p10p25p50p75p9000001

Wow, job well done internet! At least 75% of websites don’t use @import at all and even the top 90% has 1 @import at most.

For next year I’d be curious about the usage of layers, supports queries and media queries inside @import rules. This atrule is a pretty versatile beast and I wonder if we’re takin full advantage of it when we use it.

@layer

Adoption rate: 2.71% Baseline since: March 2022

000111p10p25p50p75p9000000

@layer has been baseline supported since March 2022. It’s use seems mostly powered by the use of TailwindCSS but also the use of layers like legacy and global seem encouring patterns of developers using @layer. Especially @legacy seems like a trick that developers are using to incrementally modernize their codebase.

@layer nameAdoption %Relative count
base1.85%
utilities1.80%
components1.76%
theme1.64%
properties1.48%
reset0.29%
legacy0.16%
component0.13%
global0.10%
tokens0.10%

@charset

Adoption rate: 39.57% Baseline since: July 2015

0246810p10p25p50p75p9000012

Almost 40% of websites are using @charset and an overwhelming majority uses UTF-8 encoding. But what’s interesting is that there’s a pretty clear group using Chinese (gb2312), Japanese (shift_jis) and cyrillic (windows-1251) encodings. This proves that there’s a real use-case for @charset even though most of us don’t use it that often.

CharsetAdoption %Relative count
utf-839.46%
iso-8859-10.03%
gb23120.02%
shift_jis0.02%
windows-12510.02%
euc-jp0.02%
euc-kr0.01%

@container

Adoption rate: 9.61% Baseline since: February 2023

000111p10p25p50p75p9000000

Almost 10% of websites using @container already (well, it has been Baseline since February 2023) is a pretty good number. Looking at the names that are used there’s no real pattern apart from the list of 8 hash-like gibberish names that appear in 0.16% of websites. Names like wrapper, card and welcome-panel speak more to the imagination.

Container nameAdoption %Relative count
wrapper3.38%
welcome-panel0.35%
welcome-panel-media0.35%
dfposts0.26%
card0.19%
wpforms-field-row-responsive0.18%
column0.17%
[hash] like _1f7pmjs0 (7 rows omitted for brevity)0.17%
wpforms-field-row-responsive-name-field0.15%
wpforms-field-2-columns-responsive0.13%
wpforms-field-3-columns-responsive0.13%
media0.12%
horizontal-product-card0.12%

I wonder if the media container has anything to do with the OG CSS Media Object. 🥰

@property

Adoption rate 2.67% Baseline since: July 2024

000111p10p25p50p75p9000000

@property is Baseline Newly Available since July 2024 so it makes sense that most websites haven’t picked it up yet. In that regard the current adoption ratio is already encouraging.

After analyzing the most common @property names I found that by far the most popular were TailwindCSS property names like --tw-border-style, --tw-font-weight etc. with most of them having an adoption rate between 1.63% and 0.12%. After that is a huge list of --x-[hash] properties which seem to be entirely computer-generated. It doesn’t really make sense to show the full chart here but it’s an interesting observation. My conclusion is that the most common usage of @property seems to be tied to some kind of (CSS) framework.

Rulesets

Each stylesheet consists of one or more rulesets or rules unless you only ship some atrules, like the Google Font API does. They are the corner stone of how CSS works: one or more selectors and some declarations and you have yourself a ruleset. With that in mind let’s see how most rulesets are constructed and used.

Rules per page

Distribution of percentiles of total CSS rules per website02,0004,0006,0008,000p10p25p50p75p902241,1192,8025,4209,118

Max: 210695

Selectors per rule

Distribution of percentiles of maximum CSS selectors per rule per website0102030405060708090p10p25p50p75p90613327090

(Most common = 1) award for max: 128528

Declarations per rule

Distribution of percentiles of maximum CSS selectors per rule per website050100150200250300p10p25p50p75p90172850117279

(Most common = 1) Award for max: 41620

Total rule size

When you add up selectors and declarations you get the rule size.

rule size = # of rule selectors + # of rule declarations

most common: 2 award for max: 128529

Distribution of percentiles of maximum CSS selectors per rule per website050100150200250300350400p10p25p50p75p90214874137352

Rule nesting depth

most common: 1 award for max nesting: 37

Distribution of percentiles of maximum CSS selectors per rule per website0246810p10p25p50p75p9011122

Selectors

Love them or hate them, you need selectors to target the elements you want to change whether it be for that fancy P3 color or that 90’s grunge background-image.

Totals

Looking at the number of selectors in a stylesheet gives us a quick look into the magnitude of things.

Distribution of percentiles of total CSS selectors per website02,0004,0006,0008,00010,00012,000p10p25p50p75p902721,3913,6607,33512,607

From my experience analyzing websites I expected the p90 and p75 to be a lot higher. I’m actually quite pleased with this distribution. Also, the top 10% of websites apparently have only 272 selectors or less which is also surprising. This seems like a really low number to me.

Pseudo classes

One concept that I blatantly stole from the Web Almanac is their overview of popular pseudo classes. It offers an interesting look into adopoption of newer pseudo classes and proportional use between classes.

Pseudo classAdoption %Relative count
hover95.24%
where90.56%
focus88.57%
before87.58%
after86.99%
not86.39%
last-child85.96%
first-child85.76%
active83.93%
root79.14%
nth-child76.82%
disabled58.83%
empty58.77%
checked58.54%
last-of-type57.32%
visited54.65%
nth-of-type53.19%
first-of-type52.97%
focus-visible48.20%
-ms-input-placeholder44.10%
has41.30%
focus-within41.02%
only-child36.97%
is36.31%
nth-last-child34.47%
-moz-focusring32.99%
-moz-placeholder25.68%
link25.64%
first-letter22.69%
invalid21.42%
host19.00%
indeterminate18.31%
-webkit-autofill17.04%
valid16.50%
placeholder-shown13.63%
lang11.20%
-moz-ui-invalid9.31%
target6.11%
-moz-placeholder-shown5.92%
nth-last-of-type4.41%
-webkit-full-screen3.77%
enabled3.60%
fullscreen3.59%
required2.79%
read-only2.53%
only-of-type2.50%

The 2022 Almanac listed :hover, :focus and :active as their top 3 but look at this: :where made it into the top 3! It has been Baseline available since January 2021 but this would be a good time to give yourself a pat on the back if you are a spec writer or part of the CSS Working Group. And while you’re patting: :has is used on 41% percent of websites (Baseline December 2023). That’s even higher than :is clocking in at 36% (also baseline January 2021). It would be interesting to see if the use of :matches and :any will go down as we use :is more. Regardless, these look like rock solid adoption rates if you ask me. We know that adoption of CSS features usually takes a bit and seeing these new-ish ones up there makes you proud of the language.

Looking further down the list we see :empty being used on 58% of websites. I find that surprising because I found it useful only on a handful of occasions.

Accessibility

Accessibility selectors are attribute selectors that check for the presence of [aria-*] and [role=*]. These are interesting because they tell a little bit about how accessibility is baked into the CSS. It’s never the complete story but it’s interesting nonetheless.

Distribution of accessibility selectors per website051015202530p10p25p50p75p90001621

Based on my own experience I find this number quite low. Most projects I’ve worked on have many more accessibility-focused selectors than this. Either everyone is setting their styles via regular class names (very valid) or we’re not focusing on accessibility that much.

Vendor prefixes

Vendor prefixed selectors are usually taken care of by CSS toolchains where they take your modern authored CSS and add some prefixes where necessary based on the required browser support.

Distribution of total prefixed selectors per website0102030405060708090p10p25p50p75p9007214784

Data shows that there’s not that many vendor prefixed selectors per website and I’m curious to see if that number will go down in coming years as some of them will become obsolete. But on the other hand, some browser makers are shipping new vendor prefixed selectors so we might be seeing these for years and years to come.

Specificity

Our beloved metric: specificity. It’s a shame Wes Bos isn’t butchering the pronunciation of this as much as he did before. Jokes aside, specificity is one of the most misunderstood concepts in CSS which is also a reason that so many people blogged about it and why online tools like Polypane’s Specificity Calculator exist. Some CSS analyzers get analysis wrong for specificity but luckily we don’t so we can show cool stuff like this:

Distribution of percentiles of total unique CSS selector specificities per website0102030405060708090p10p25p50p75p901222395883

The chart shows that 50% of websites have up to 39 unique specificities on their pages which is a higher number than I had expected. Thinking about this a bit more actually leads me to think that this might be because CSS is such an expressive, capable language: there are so many ways to express selector intent and increasingly more with new selectors like :has() and :where().

Now let’s looks at the most commonly used specificities:

SpecificityAdoption %Relative count
0,1,097.06%
0,0,196.21%
0,1,196.14%
0,0,095.84%
0,2,094.57%
0,2,193.70%
0,3,091.00%
0,1,290.32%
0,3,188.96%
0,2,287.93%
0,4,085.65%
0,0,285.25%
0,3,281.94%
0,4,181.79%
1,0,079.28%
0,5,077.21%
0,1,376.62%
0,2,375.33%
1,1,072.63%
0,5,169.84%
0,4,269.50%
0,6,067.87%
0,3,366.32%
1,0,165.09%
1,1,164.69%
1,2,063.34%

There’s absolutely nothing notable about this adoption rate chart. This is to be expected. One item I want to highlight is the no. 4 position of 0,0,0. This means that the high usage of :where() seems to translate to this chart as well, as well as it being very likely that most websites use the universal selector (*). The fun part is at the bottom (the list has 2,876 unique entries) where things definitely got out of hand.

TODO: insert link to source data

Selector complexity

Most common selector complexity per website:

Distribution of percentiles of most common CSS selector complexity per website0246810p10p25p50p75p9011123

Most selectors on most sites are simple selectors: only 1 or 2 parts to them. But what about the most complex selectors on any site?

Distribution of percentiles of maximum CSS selector complexity per website051015202530p10p25p50p75p90812162229

A different picture. Even simple websites sometimes need more complex selectors to express more complex state.

Combinators

Selector combinators let you define relationships between your selectors. We often use the descendant combinator without thinking about it, but what about the others?

Distribution of percentiles of total CSS atrules per website0%0%0%100%100%100%(descendant)> (child)+ (adjecent sibling)~ (general sibling)96.20%90.23%81.94%67.68%

As expected the descendant combinator takes the top spot but the child combinator (>) is a close second. The other two are a bit below that and for me that makes sense. That seems to align with how I write CSS myself.

Declarations

!important usage

Distribution of percentiles of total !importants per website02004006008001,0001,2001,4001,600p10p25p50p75p901361546281,514

Max is 249021

Distribution of percentiles of total !importants per website0%0%0%0%0%p10p25p50p75p900%1%2%6%13%

Custom properties

Distribution of percentiles of total CSS custom properties per website02004006008001,0001,200p10p25p50p75p9000683391,161 Distribution of percentiles of custom properties ratio0%0%0%0%0%p10p25p50p75p900%0%1%5%14.00%

Property browserhacks

Distribution of percentiles of total CSS atrules per website0%0%0%0%0%*property_property/property#property+property$property18.75%3.52%1.17%0.31%0.03%0.02%

Vendor prefixed properties

Distribution of percentiles of total CSS custom properties per website02004006008001,0001,200p10p25p50p75p906401405041,274 Distribution of percentiles of custom properties ratio0%0%0%0%0%p10p25p50p75p900%1%2%6%11%

Values

Let me start with a rather disappointing note about this chapter. Because analyzing values is one of the areas where Project Wallace shines but I made a bad decision early on in the scraping process. After an initial crawl started to fill my laptop’s drive rather quickly I decided to cut out some ‘non-important’ metrics to save some disk space and speed up analysis. Boy, do I regret that. This decision means that I’ve analyzed and then thrown away all analysis about popular colors, font-sizes, shadows, everything. All I have at this point is aggregates percentiles. So. This is not the values chapter I was hoping for but it’s the best I can do for this year.

Colors

Unique colors are analyzed by looking at their string representation so Red is a different value than red and #f00. This is what is counted when we look at the metric like that:

Distribution of percentiles of total unique colors per website0100200300400500p10p25p50p75p902677164278408

It would be very interesting to compare colors value-wise so it’s on my list for next year.

Then on to color formats. This table highlights what the most used color formats are across all websites analyzed.

Color formatAdoption %Relative count
hex697.10%
hex394.94%
transparent90.65%
rgba90.44%
named79.24%
currentcolor59.29%
rgb55.26%
hex842.32%
hex433.86%
hsla32.08%
system23.08%
hsl6.20%
oklch1.89%
color0.56%
oklab0.54%
lab0.24%
lch0.05%
hwb0.03%

The top 7 is very much as expected bt I’m surprised that 8-character hex colors are catching on so well with 42% adoption. Although I might be an old-school dev because apparently this and 4-character hex codes are baseline widely available since January 2020…

Further down the list we still HSL(A) still beating OKLCH by some margin despite active campaigns to get us onto the better format.

Font-families

Apart from declaring custom @font-face families there’s also the point of using actual families for your styling. How many unique families do websites use in their CSS?

Distribution of percentiles of total unique font-families per website0510152025303540p10p25p50p75p9036122033

This data actually overlaps quite well the use of @font-face with the amount of families used always being slightly higher than the amount of custom families declared.

Font-sizes

Similar to the color analysis we only compare font-sizes by their string representation so 1.2em is not the same as 120% in our analysis even though the browser will render them the same.

Distribution of percentiles of total unique font-sizes per website020406080100p10p25p50p75p901226477399

This table matches my expectations quite well but I’m always slightly surprised how we end up with so many unique font-sizes on our websites. There are countless articles out their explaining how to create a font scale and use that but I guess reality comes at us quickly when it comes to CSS.

Line-heights

Line-heights are often, but not always set in combination with font-size and/or font-family so it is no surprise that this graph is similar in shape to the one of them but just lower in numbers.

Distribution of percentiles of total unique line-heights per website010203040506070p10p25p50p75p90515284666

Box-shadows

One metric that is under-analyzed but often used in design systems is the humble box-shadow. Not contibuting to the box model, but it does play a role in branding and UX.

Distribution of percentiles of total unique box-shadows per website0102030405060p10p25p50p75p9015173460

Text-shadows

Like box-shadows, text-shadows can help with creative effects or even help improve readability of text on top of images (wow, that CSS-Tricks article is from 2014!).

Distribution of percentiles of total unique text-shadows per website0246810p10p25p50p75p9000137

The use of text-shadows appears to be very limited. It makes me wonder why that is. Don’t we see the value in it? Is it too distracting?

Z-indexes

Distribution of percentiles of total unique z-indexes per website0510152025303540p10p25p50p75p90210182838

Animation durations

Oh, I wish that I could look into the actual values used for animation-duration. It would be very interesting to see what durations are used most so we could spark a healthy debate on social media as to why everyone is wrong.

Distribution of percentiles of total unique animation durations per website051015202530p10p25p50p75p9016121929

At least we know that most websites don’t use a lot of unique durations. Again, these are compared string-wise so 200ms is different from 0.2s and .2s. Now fight.

Animation timing functions

Are timing-functions debated as heavily as durations? At least there are fewer unique values per website so perhaps this is less of an ‘issue’?

Distribution of percentiles of total unique timing functions per website05101520p10p25p50p75p901361016

Vendor previxed values

Distribution of percentiles of total unique line-heights per website050100150200p10p25p50p75p90042076199

Value resets

Credits to Ana.

Distribution of percentiles of total unique line-heights per website0100200300400500600700p10p25p50p75p901471200385664

Units

Distribution of percentiles of total unique units per website05101520p10p25p50p75p904791112
UnitAdoption %Relative count
px98.28%
em91.72%
s91.50%
deg89.30%
rem80.15%
vh76.04%
vw72.34%
fr60.23%
ms56.70%
turn35.64%
ch23.06%
pt15.50%
dvh10.32%
ex7.82%
cm7.19%
svh3.98%
lh2.80%
x2.75%
dvw2.30%
vmax1.69%
vmin1.52%
cqw1.48%
pc1.29%
mm1.18%
in1.08%
lvh0.95%
svw0.55%
cqi0.55%
cqh0.32%
cap0.15%
vi0.15%
lvw0.14%
dppx0.13%
m0.11%
rad0.08%
cqmin0.07%

Research method

This article used the following methodology:

All conclusions and opinions are mine, a mere mortal with an above average interest in looking at CSS in a different way than most people do. You may not agree and that’s fine.