The CSS Specificity Problem Nobody Talks About

You've written a perfectly valid CSS rule. You've checked the syntax. You've confirmed the selector matches the element. You've cleared your cache, opened an incognito window, and stared at the screen for ten minutes. The style isn't applying. Or worse, it was applying yesterday and now it isn't.

If you've been writing custom CSS in Squarespace for any length of time, you've been here. And if you solved the problem by adding !important to the end of the declaration and moving on with your life, you're not alone. But you also didn't solve the problem. You buried it, and it will come back.

This article is about why that happens, how Squarespace's CSS actually works under the hood, and how to write custom styles that win consistently without brute-forcing your way through with !important on every line.

How Specificity Actually Works

Most designers learn specificity as a rough hierarchy: inline styles beat IDs, IDs beat classes, classes beat elements. That's not wrong, but it's incomplete, and the incomplete version is what causes most of the confusion when working inside Squarespace.

CSS specificity is calculated as a three-part score, sometimes written as (a, b, c). The "a" column counts ID selectors. The "b" column counts class selectors, attribute selectors, and pseudo-classes. The "c" column counts element selectors and pseudo-elements. The browser compares these scores left to right, and the higher score wins at each level.

So .blog-item-title has a specificity of (0, 1, 0). One class, nothing else. Meanwhile #page .blog-item-title has a specificity of (1, 1, 0). One ID plus one class. The second selector will always beat the first, regardless of where they appear in the stylesheet, because the ID column is evaluated before the class column. A thousand classes cannot outweigh a single ID.

This matters enormously in Squarespace because the platform's own stylesheets are full of high-specificity selectors. Squarespace doesn't style elements with simple, low-specificity classes the way you might in a clean custom build. It uses deeply nested selectors, often chaining multiple classes and sometimes including IDs, which means the specificity bar you need to clear with your custom CSS is already high before you write a single line.

How Squarespace Loads Its CSS

Understanding the loading order helps explain why your custom CSS sometimes works and sometimes doesn't.

When a Squarespace page renders, stylesheets load in a specific sequence. The platform's base styles come first, establishing the default appearance of every element. Then the template-specific styles load, applying the visual characteristics of whichever template family your site uses. After that come the Site Styles settings, the visual controls you adjust in the Design panel, which Squarespace compiles into CSS and injects inline. Finally, your custom CSS (entered via the CSS Editor or code injection) loads last.

In theory, this loading order should work in your favour. CSS follows a "last one wins" rule when two selectors have equal specificity, so your custom CSS, loading last, should override everything above it. The problem is that “equal specificity" clause. If Squarespace's own selector for a heading is something like .header-nav-wrapper .header-nav-item a (specificity: 0, 3, 1) and you're targeting the same element with just .header-nav-item a (specificity: 0, 1, 1), you lose. Your selector is less specific, and loading order doesn't matter when specificity differs.

The Inspector Is Your Best Friend

Before writing any custom CSS in Squarespace, you should be inspecting the element you want to style using your browser's developer tools. Right-click the element, choose "Inspect," and look at the Styles panel on the right side. This shows you every CSS rule currently affecting that element, in order of specificity, with overridden rules struck through.

What you're looking for is the existing selector that's applying the style you want to override. Copy that selector. Not a simplified version of it, not what you think it should be, but the actual selector Squarespace is using. If you match that selector exactly, you have equal specificity, and because your custom CSS loads after the platform's stylesheets, you win. If you need a guaranteed override, add one additional class or attribute to your selector to push your specificity one point higher.

For example, if the existing rule is:

.header-menu-nav-wrapper a {
  color: #000000;
}

Then your override should be at minimum:

.header-menu-nav-wrapper a {
  color: #FF5733;
}
That's equal specificity, and your rule loads later, so it wins. But if you want to be certain, particularly if you're dealing with multiple stylesheets or third-party scripts that might inject CSS after yours, you could write:
.header-menu-nav-wrapper a.header-menu-nav-item {
  color: #FF5733;
}

That's one class higher in specificity and will beat the original rule regardless of loading order.

Why !important Is Almost Always Wrong

The !important declaration overrides all specificity calculations. It's the nuclear option. And like most nuclear options, it solves the immediate problem while creating bigger problems downstream.

The first issue is maintenance. Once you use !important on a rule, the only way to override that rule later is with another !important on a more specific selector. This creates an escalation pattern where your stylesheet becomes a specificity arms race, each new rule needing to be more aggressive than the last. Six months in, you have a CSS file full of !important declarations and no clear way to understand which rules actually take precedence.

The second issue is Squarespace's own responsive behaviour. The platform applies different styles at different breakpoints, and some of those responsive adjustments use !important themselves. When your custom CSS also uses !important, you can end up with competing !important declarations where the winner is determined by specificity again, bringing you right back to the problem you were trying to avoid.

The third issue is specificity: if Squarespace updates its stylesheets (which happens regularly, often without notice), new selectors with !important can override your !important rules if they have higher specificity. You've built on sand.

The only contexts where !important is defensible are: overriding inline styles applied by JavaScript (which have maximum specificity and can't be beaten any other way), and quick, temporary fixes that you plan to replace with properly scoped rules. If you're using it for anything else, you're accumulating technical debt.

Squarespace-Specific Selector Patterns

Squarespace 7.1 uses a consistent set of naming conventions that, once you learn them, make targeting elements much more predictable.

Section wrappers follow the pattern .page-section with a unique data attribute for each section.

You can use [data-section-id="your-section-id"] as a scoping mechanism to target elements within a specific section without affecting the same element type elsewhere on the page. The section ID is visible in the URL when you click "Edit Section" in the editor, or in the page source.

Collection items (blog posts, portfolio items, products) use .blog-item, .portfolio-item, and .product-item prefixes respectively. Individual items can be targeted using their URL slug as a body class, which Squarespace adds automatically. So if you have a blog post with the slug my-first-post, the body tag will include a class you can use to target styles on that specific post's page.

The Fluid Engine uses .fluid-engine as a wrapper class, with .fe-block for individual blocks. This is important because Fluid Engine sections and Classic Editor sections have different DOM structures, and CSS that works for one may not work for the other.

Header and footer elements use .header- and .footer- prefixes. The navigation specifically uses .header-nav-wrapper, .header-nav-list, and .header-nav-item in a nested structure that often requires three or four classes to achieve sufficient specificity.

Writing CSS That Lasts

Given everything above, here's an approach to custom CSS in Squarespace that minimises specificity conflicts and maintenance headaches.

Start every rule by inspecting the existing selector. Match it or exceed it by one class. Never guess at selectors based on what you think the HTML structure should be, because Squarespace's DOM is generated dynamically and doesn't always follow predictable patterns.

Scope aggressively. If a style should only apply to one section, use the section's data attribute as the outermost selector. If it should only apply to one page, use the page's body class (visible in the Inspector on the <body> tag). The more precisely scoped your CSS is, the less likely it is to collide with platform styles or your own future additions.

Use CSS custom properties (variables) for values you'll reuse. Squarespace 7.1 supports custom properties fully, and defining your colours, fonts, spacing values, and breakpoints as variables at the top of your custom CSS file means you can update them in one place rather than hunting through dozens of rules.

/* Custom properties defined once */
:root {
  --brand-primary: #2C3E50;
  --brand-accent: #E74C3C;
  --spacing-section: 80px;
  --font-heading: 'Your Font', serif;
}

/* Used throughout */
.header-title-text a {
  color: var(--brand-primary);
  font-family: var(--font-heading);
}
Comment your code. Not "this changes the header colour" level comments, but "this overrides the default nav link colour because the client's brand requires X" level comments. Future you, or the next designer who inherits this site, needs to understand not just what the code does but why it exists.

Test at every breakpoint. Squarespace 7.1 uses a primary breakpoint at 768px (the tablet/mobile threshold) but also applies subtle layout adjustments at other widths. Your custom CSS may behave differently at 1024px, 800px, and 640px even if you haven't written any media queries yourself, because the platform's responsive rules are interacting with yours. Check every major width before you call a style finished.

Finally, audit regularly. Squarespace pushes platform updates that can change selector names, add new classes, or alter the specificity of existing rules. An annual review of your custom CSS, checking each rule still applies correctly, takes an hour and can save days of debugging later.

The Bigger Principle

Specificity isn't a Squarespace problem. It's a CSS problem that Squarespace makes more visible because you're writing your code on top of someone else's stylesheet rather than starting from scratch. Understanding how specificity actually works, not the simplified version but the real calculation, is one of the things that separates a designer who can make CSS do what they want from a designer who spends half their time fighting it.

If you take one thing from this article, let it be this: stop guessing at selectors and stop reaching for !important. Open the Inspector, find the existing rule, match its specificity, and write clean, scoped, well-commented CSS that you'll still understand six months from now. Your future self will thank you. So will whoever maintains the site after you.


Related Articles

Dave Hawkins // Made by Dave

As a top tier Squarespace Expert and founder of Made by Dave, I bring over 10 years of Squarespace experience and 600+ bespoke website launches. Our process combines consultancy, design, project management and development for a collaborative and efficient experience with clients like you. Whether you need a new website or updates for your existing site, we'll help you get up and running.

https://madebydave.org
Previous
Previous

DNS for Designers Who Pretend to Understand DNS