Is CSS a programming language?
November 29, 2024 8 minutes reading time Development tools css
Whether CSS qualifies as a programming language has long been a topic of debate, particularly among developers from different disciplines, such as frontend versus backend. To address this question, let’s dissect it from several angles: definitional, functional, semantic, and pragmatic. By the end, I try to synthesize these perspectives into a coherent conclusion.
I’m a backend developer specializing in building and maintaining backend services that integrate SQL databases and other APIs.
I long thought that CSS was not a programming language. But recently I have reconsidered this idea.
Mainly because I saw what “Fluid Layout” means and how e.g. functions like clamp
and minmax
can be used.
What Defines a Programming Language?
To determine if CSS is a programming language, we must first clarify what constitutes one. Generally, programming languages share the following characteristics:
- Syntax and Structure: A defined set of rules and grammar for writing code.
- Turing Completeness: The ability to represent a computation that can be performed by a Turing machine (e.g. loops, conditionals, and variables).
- Logic and Control Flow: Mechanisms to make decisions and repeat operations (e.g.
if
conditions,for
loops). - Abstraction: The ability to encapsulate logic into reusable components (e.g. functions or classes).
- Manipulation of State: The ability to change or operate on data values over time.
CSS and These Characteristics:
- Syntax and Structure: CSS undeniably has a well-defined syntax for styling HTML elements. Selectors, properties, and values form the core syntax.
- Turing Completeness: CSS itself is not Turing complete because it lacks constructs like loops and conditionals in
its original form. However, modern CSS has introduced functions and features (like
calc()
,clamp()
,@media
, and@supports
) that allow for a more dynamic and conditional design, bringing it closer to computational logic. - Logic and Control Flow: CSS has conditional-like constructs in the form of media queries and feature queries
(
@media
,@supports
), but these do not represent general-purpose logic flow. - Abstraction: CSS supports abstraction to some extent via reusable rules (e.g. variables with
--custom-properties
, mixins in preprocessors like SASS, and shared class-based design patterns). - State Manipulation: CSS interacts with state indirectly (e.g. pseudo-classes like
:hover
,:focus
, and:checked
), but it doesn’t directly manipulate values or states in the way imperative languages do.
Thus, CSS satisfies some but not all characteristics of traditional programming languages.
CSS as a Declarative Language
CSS is generally categorized as a declarative language, meaning it describes what should happen rather than providing explicit instructions on how to make it happen. For example:
div {
color: red;
display: flex;
}
Here, you declare that div
elements should be red and laid out as flex containers. You don’t specify how the
browser engine achieves this result - this is abstracted away.
Declarative languages, including SQL are typically not considered programming languages in the strictest sense. They lack imperative constructs like loops or explicit state management. However, declarative languages are undeniably powerful tools in their respective domains.
Recent Enhancements to CSS
Modern CSS introduces features that challenge its purely declarative nature:
Functions, e.g. clamp, calc, and minmax
These functions allow you to perform calculations directly within the CSS, enabling dynamic behavior based on runtime conditions:
font-size: clamp(1rem, 2vw, 3rem);
grid-template-columns: minmax(250px, 1fr) 2fr;
These functions exhibit computational behavior, a hallmark of programming. However, they are limited to numerical computations and cannot branch or loop.
Conditional Logic, e.g. @media and @supports
Media queries @media
and feature queries @supports
provide conditional logic in CSS. An example:
.section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
@supports (display: masonry) {
display: masonry;
masonry-template-tracks: repeat(auto-fit, minmax(250px, 1fr));
}
}
As you can see …
Whoa, whoa, whoa! Hold up, Code Captain! You just tossed out a CSS snippet like it’s a casual Tuesday, but let’s give this masterpiece the attention it deserves, shall we?
This isn’t just any grid - it’s a fancy-pants grid with a hidden agenda. Let’s unpack this brilliance, step by step!
Yes, you’re absolutely right. We should spend some time with this thing!
Let’s break down this CSS step by step to understand what each line does, including the use of the masonry
feature:
Base Rules for the section element
.section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
display: grid;
- Sets the
.section
element to use CSS Grid for its layout. - CSS Grid is a powerful layout system that arranges content into rows and columns, allowing precise control over alignment and spacing.
- Sets the
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr))
repeat(auto-fit, ...)
:- Automatically determines the number of columns that can fit in the available space.
auto-fit
ensures that grid items expand to fill the row even if there aren’t enough items to fill all columns.
minmax(250px, 1fr)
:- Each column is at least
250px
wide (the minimum size) but can expand to fill available space (up to1fr
), wherefr
stands for a fraction of the available free space.
- Each column is at least
This combination creates a responsive layout where the number of columns adjusts based on the container width, and columns are sized dynamically within the defined constraints.
gap: 1rem
- Adds spacing of
1rem
between grid items in both rows and columns.
- Adds spacing of
Masonry-Specific Rules Inside @supports
@supports (display: masonry) {
display: masonry;
masonry-template-tracks: repeat(auto-fit, minmax(250px, 1fr));
}
@supports (display: masonry) { ... }
- The
@supports
rule checks whether the browser supports themasonry
value for thedisplay
property. - If
masonry
is supported, the rules inside the block are applied.
- The
display: masonry
- Sets the layout to
masonry
, a Firefox and Safari feature with some limitations at the time of writing. - Masonry layouts arrange items in a staggered, vertical pattern, much like how Pinterest displays content.
- Sets the layout to
masonry-template-tracks: repeat(auto-fit, minmax(250px, 1fr))
- Defines how the masonry layout organizes its columns:
repeat(auto-fit, ...)
: Similar to the grid layout, automatically determines the number of columns that can fit in the container.minmax(250px, 1fr)
: Ensures each column is at least250px
wide but can expand proportionally to fill the remaining space (1fr
).
- Defines how the masonry layout organizes its columns:
This rule customizes the behavior of the masonry layout to align with the grid’s column definitions, ensuring a consistent visual structure regardless of whether masonry is supported.
Summary of Behavior
Default Layout (Grid):
- By default, the
.section
uses CSS Grid. - The number of columns dynamically adjusts based on the available space, ensuring responsive behavior.
- Items are laid out in a strict row-and-column structure with equal gaps (
1rem
) between them.
- By default, the
Enhanced Layout (Masonry):
- If the browser supports the
masonry
display value, the layout switches to a masonry-style layout. - Items are stacked vertically in staggered columns, but the column sizes are still defined using the same
minmax(250px, 1fr)
logic.
- If the browser supports the
Fallback and Progressive Enhancement:
- Browsers that do not support
masonry
fall back to the standard grid layout, ensuring the layout remains functional and visually appealing.
- Browsers that do not support
Key Advantages of This Code
- Responsive Design: The use of
repeat(auto-fit, minmax(...))
makes the layout responsive to container width. - Progressive Enhancement: The
@supports
block ensures the enhanced masonry layout is only applied in browsers that support it, while maintaining compatibility with other browsers. - Visual Consistency: Both the grid and masonry layouts share the same column-sizing logic, minimizing discrepancies between supported and unsupported browsers.
This code demonstrates modern CSS practices for creating flexible, responsive, and progressively enhanced layouts.
Now, back to our main text.
These constructs resemble if
statements but operate declaratively - they describe what to apply under certain
conditions, without actively controlling flow.
Pseudo-Classes and States
CSS pseudo-classes like :hover
, :focus
, and :nth-child()
allow for dynamic styles based on user interaction or
element position. While they can produce intricate designs, they don’t directly manipulate state or offer procedural
control flow.
Preprocessors, e.g. SASS, LESS
While not CSS itself, preprocessors extend CSS with imperative constructs like loops, variables, and functions:
@for $i from 1 through 5 {
.box-#{$i} {
width: $i * 10px;
}
}
This introduces procedural programming concepts but requires a compilation step before outputting standard CSS.
The Case for CSS as a Programming Language
If you reconsider the core purpose of CSS, you might argue it performs programming-like tasks. A programming language does not necessarily need to be imperative or Turing complete to be considered one - it must simply provide mechanisms to solve problems or express computations in its domain.
CSS increasingly enables developers to solve complex layout and styling problems programmatically:
- The introduction of CSS Grid and Flexbox makes layout a computational exercise.
- Functions like
clamp()
andcalc()
blur the lines between styling and computation. - The growing interactivity provided by pseudo-classes and conditional logic approaches stateful behavior.
From this perspective, CSS can be seen as a domain-specific programming language for styling.
The Pragmatic Perspective
Labels matter less than functionality. Even if CSS is not a “programming language” in the strictest sense, it has evolved into a powerful, essential tool for web development. It operates in tandem with HTML and JavaScript, and modern features make it more dynamic and computational than ever before.
For backend developers like myself, the growing sophistication of CSS feels closer to programming because its
features now demand a deeper understanding of computational logic. Terms like “fluid layouts” and functions like
clamp()
require thought processes akin to solving problems with code.
But What About the Initial Question?
CSS is not traditionally classified as a programming language, as it lacks imperative control structures and state manipulation. However, it shares several characteristics of domain-specific programming languages (e.g. SQL) and has evolved to include computational and conditional capabilities.
If we loosen the definition of a programming language to include declarative and domain-specific tools, CSS could qualify as a programming-adjacent language. Whether you call it a programming language or not, its modern capabilities demand respect and computational thinking.