One of the ways you can classify a programming language is by how strongly or weakly typed it is. Here, “typed” means if variables are known at compile time. An example of this would be a scenario where an integer (1
) is added to a string containing an integer ("1"
):
result = 1 + "1";
The string containing an integer could have been unintentionally generated from a complicated suite of logic with lots of moving parts. It could also have been intentionally generated from a single source of truth.
Despite the connotations that the terms “weak” and “strong” imply, a strongly-typed programming language isn’t necessarily better than a weakly-typed one. There may be scenarios where flexibility is needed more than rigidity, and vice-versa. As with many aspects of programming, the answer is dependent on multiple external contexts (i.e “it depends”).
The other interesting bit is there is no formal definition of what constitutes strong or weak typing. This means that perceptions of what is considered a strongly or weakly-typed language differ from person to person, and may change over time.
TypeScript
JavaScript is considered a weakly-typed language, and this flexibility contributed to its early adoption on the web. However, as the web has matured and industrialized, use cases for JavaScript have become more complicated.
Extensions like TypeScript were created to help with this. Think of it as a “plugin” for JavaScript, which grafts strong typing onto the language. This helps programmers navigate complicated setups. An example of this could be a data-intensive single page application used for e-commerce.
TypeScript is currently very popular in the web development industry, and many new projects default to using TypeScript when first setting things up.
Compile time
Compile time is the moment when a programming language is converted into machine code. It is a precursor to runtime, the moment when machine code is performed by the computer.
As with many things on the web, compile time is a bit tricky. A setup that utilizes TypeScript will stitch together component pieces of JavaScript code and compile them into a single JavaScript file for the browser to read and run.
The time when component pieces compile is when they are all combined. TypeScript serves as a kind of overseer, and will yell at you if you try to break the typed conventions you have set up for yourself before combination occurs.
The stitched-together JavaScript file is then ingested by the browser, which has its own compile time. Browser compile time is highly variable, depending on:
- The device the browser is on,
- What other work the browser is doing, and
- What other work the device’s other programs are doing.
TypeScript isn’t directly used by the browser, but its presence is felt. JavaScript is fragile. TypeScript helps with this fragility by trying to prevent errors upstream in the code editor. This lessens the chance errors occur in the JavaScript read by the browser — errors that would cause JavaScript to stop functioning on the website or web app a person is using.
CSS
CSS is a declarative, domain-specific programming language. It is also strongly typed. For the most part, values in CSS stay declared as authored. If a value is invalid the browser throws the entire property away.
Types in CSS
The list of types in CSS is thorough. They are:
Textual types
- Globally-scoped keywords:
initial
inherit
unset
revert
- Custom identifies, which are specifically used for things, such as providing a
grid-area
name - Strings, such as,
"hello"
- URLs, such as
https://css-tricks.com/
- Dashed idents (
--
), which are used to create custom properties (more on this in a bit)
Numeric types
- Integers, which are decimal numbers 0–9
- Real numbers, such as
3.14
- Percentages, such as
25%
- Dimensions, a number with a unit appended to it such as (
100px
or3s
) - Ratios, such as
16/9
- flex, a variable length for CSS grid calculation
Quantity types
- Lengths:
- Absolute lengths, such as pixels or centimeters
- Relative lengths, such as root ems or the viewport height
- Time, such as
200ms
- Angles, such as
15deg
- Time, such as
250ms
- Frequencies, such
16Hz
- Resolution, such as
96dpi
Dimensions and lengths might seem similar, but dimensions can contain percentages and lengths cannot.
Color types
- Keywords:
- Named colors, such as
papayawhip
transparent
currentColor
- Named colors, such as
- RGB colors:
- Hexidecimal notation, such as
#FF8764
- RGB/RGBa notation, such as
rgba(105, 221, 174, 0.5)
- Hexidecimal notation, such as
- HSL/HSLA colors, such as
hsl(287, 76%, 50%)
- System colors, such as
ButtonText
Image types
- Image, which is a URL reference to an image file or gradient
color-stop-list
, a list of two or more color stops, used for gradient notionlinear-color-stop
, a color and length expression used to indicate a gradient color stoplinear-color-hint
, a length percentage used to interpolate colorending-shape
, which uses a keyword of eithercircle
orellipse
for radial gradients
2D positioning types
- Keywords:
top
right
bottom
left
center
- A percentage length, such as
25%
Programming in CSS
The bulk of programming in CSS is authoring selectors, then specifying a suite of properties and their requisite values. Collections of selectors give content a visual form, much as how collections of JavaScript logic creates features.
CSS has functions. It can perform calculation, conditional logic, algorithmic expressions, state, and mode-based behavior. It also has custom properties, which are effectively CSS variables that allow values to be updated dynamically. Heck, you can even solve fizzbuzz with CSS.
Like other programming languages, there is also a “meta” layer, with different thoughts and techniques on how to organize, manage and maintain things.
Throwing errors
Unlike other programming languages where code largely exists under the hood, CSS is highly visual. You won’t see warnings or errors in the console if you use an invalid value for a property declaration, but you will get visuals that don’t update the way you anticipated.
The reason for this is that CSS is resilient. When visuals don’t update because of a misconstructed declaration, CSS is prioritizing, ensuring content can be shown at all costs and will render every other valid declaration it possibly can. This is in keeping with the design principles of the language, the principles of the platform, and the overarching goals of the web’s mission.
Proof
Let’s demonstrate how strong typing in CSS keeps the guardrails on in three examples: one with a straightforward property/value declaration, one with calculation, and one with redefining a custom property.
Example 1: Straightforward property/value declaration
See the Pen Basic example by Eric Bailey (@ericwbailey) on CodePen.
For this example, the browser does not understand the banner’s border-style
“potato” declaration. Note that the other .banner
class selector property/value declarations are honored by the browser and rendered, even though border-style
has a type mismatch. This is an example of how resilient CSS is.
The border-style
declaration is expecting one of the following textual style types:
- Globally-scoped keywords, or a
- Dashed indent for a custom property.
If we update border-style
to use a valid, typed value of dotted
, the browser will render the border!
Example 2: Calculation
The calc()
function in CSS allows us to take two arguments and an operator to return a calculated result. If one of the arguments doesn’t use a valid type, the calculation won’t work.
In this Pen, the p
selector’s font-size
property is expecting a value with a numeric dimension type (e.g. 1.5rem
). However, the calculation function produces an invalid type value for the font-size
property. This is because the second argument in the calc()
function is a string ("2rem"
), and not a numeric dimension type.
Because of this, the paragraph’s font size falls back to the next most applicable parent node — the font-size
of 1.5rem
declared on the body
element.
This is a bit in the weeds, but worth pointing out: Combining two custom properties in a calc()
function can cause errors. While both custom properties may be valid on their own, calc()
will not accept dashed indent textual types. Think of a scenario where we might try multiplying custom properties that contain mismatched units, e.g. --big: 500px
and --small: 1em
.
Example 3: Redefined custom property
Like JavaScript variables, custom property values can be redefined. This flexibility allows for things like easily creating dark mode color themes.
In the :root
selector of this CodePen, I have set a custom property of --color-cyan
, with a value of #953FE3
. Then, in the .square
class, I have updated the --color-cyan
custom property’s value to be top
. While top
is a valid, typed value, it is not a type that background-color
honors.
Notice that the updated custom property is scoped to .square
, and does not affect other usages, such as the right-hand border on the phrase “Don’t play to type.” And if you remove the redefined custom property from .square
, you’ll see the cyan background color snap back in.
While this is a bit contrived, it serves as an example of how redefining custom properties can get away from you if you’re not careful.
This phenomenon can be found in projects with poor communication, larger CSS codebases, and situations where CSS preprocessors are used to construct custom properties at scale.
Tooling
With the gift of hindsight, I think a lack of console warnings for CSS is a flaw, and has contributed to a lot of the negative perceptions about the language.
Hoping a developer will notice a potentially tiny visual change is too big an ask, and does not meet them where they are for most of their other daily tools. There are a couple of initiatives I’m aware of that try to address this.
First is stylelint, a linter made specifically to deal with CSS and CSS-like preprocessing languages. stylelint can integrate with code editors, task runners, command line tools, and GitHub actions to help keep your CSS under control. This allows it to meet developers where they already are.
Second is Firefox’s excellent suite of CSS inspection options in its Developer Tools. In particular, I would like to call attention to its ability to identify unused CSS. This is extremely helpful for identifying selectors that may have run afoul of a type mismatch.
Wrapping up
CSS has been strongly typed for as long as it has been a programming language, and as a programming language it has been around for a long time. It’s also done a lot of growing up lately. If you haven’t checked in, there are some new, amazing features available.
As strongly-typed JavaScript becomes more popular, it is my hope that it helps developers become more comfortable with the firm, yet flexible approach of CSS.
Thank you to Miriam Suzanne for her feedback.
Great article! I really like this pragmatic approach when teaching css stuff. You treat it as an actual programming languages and not a set of properties you can tweak to make your text a different color
Except CSS is not a programming language. You cannot create a program with CSS. CSS is a data language (like Json or YAML).
This take is as boring as it is predictable.
It might be boring, but it’s part of the foundation of this article.
Here’s what the official WWW consortium website says: “Cascading Style Sheets (CSS) is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents.” https://www.w3.org/Style/CSS/Overview.en.html
And here’s an article about configuration files:
https://en.m.wikipedia.org/wiki/Configuration_file
CSS is just a set of config files for the rendering engine.
That’s how I view it.
You could consider it to actually be such one.
This is because it has as input the markup itself and variables the values from the properties. It actually does take the non-styled html and transforms it into a really good looking webpage.
For this reason, when teaching my students,I often tell them that what’s inside the curly braces can be considered DIRECTIVES much more than mere powerless declarations. It’s like when calling a function in JS (of course, here the browser executes the directive –please note the imperative meaning of it–, just by loading the page, thus not necessarily triggered by any human related event occurred in that page, as hover, click, whatever).
We actually write CSS code to be ran, not only to be looked at it in our code editor.
This maybe adds to the fact of CSS being a programming language after all. It’s not just something presentational as a notepad text, where some syntax in it could imply that you could navigate into another document should you open the first one (obviously I refer to a html one) in a browser. It actually has associated a kind of runtime too, so to speak, when the browser decides to flow (or reflow) the page. And the fact that it’s cascading proves this fact (the browser fills the body from outside towards inside on each element, aka from the parent to children, therefore there is a strict order in doing this, having a conditional logic /if then /or else/ like any other languages. Fact is that JS or any languages work in cascading mode too (nothing new here), because you can’t properly use a variable value until it’s assigned, too. Therefore there is a solid cascading logic under the hood, too.
Therefore the CSS isn’t just presentational, as Gioconda isn’t either presentational because there was needed some painter to mix the colour on the canvas, don’t you agree? Therefore there was a transformation (as any other programming language is meant to facilitate for us), and the fact that CSS works in the browser doesn’t decrease its importance. By the way, all languages that need someone to “help them work” (aka compile them before actually running any code from them) aren’t at all somehow lower class citizens of the programming world.
I could say it’s even the contrary.
@Eve Let me give you an example,
Let’s say we made a program in JavaScript and kept a json file for configuration (to store the values of some key properties), tweaking values in this json may strongly affect the output of our code but that doesn’t mean json is a programming language.
Before this goes on too far, it seems some people are conflating Turing completeness with programming language. As linked to in the post, domain-specific languages (DSL) are a recognized discipline within computer science.
CSS, like JSON, JavaScript, HTML, etc. are a set of instructions you feed to a compiler to get a result. That’s programming. Creating a program is only one of many kinds of output a programming language can accomplish.
When you question CSS as a programming language, you’re going against the entire field of computer science. Unless you’ve got a doctorate in the field, I’m not likely to listen.
Again, this take is boring and mediocre, and I don’t want to really give it any more time or attention. A far better question is what have you done recently in CSS to move the conversation forward?
Even custom properties can have a type:
https://developer.mozilla.org/en-US/docs/Web/CSS/@property
It’s pretty new and only supported in Chromium browsers.
With setting the type for a custom property, the browser will also be capable of animating values such as background-gradients. See the examples on: https://web.dev/at-property/
Thanks for the post! It’s worth noting that you can accomplish some kind of type conversion in CSS, too. Here are a few examples:
* Cassie Evans converts an integer to a string using
counter-reset()
* Carter Li shows how to convert a float to an integer using both
calc()
and@property
.There might be more possible conversions, not quite sure — but I often rely on those in my own HTML/CSS charts experiments (eg. to display value in a fake tooltip)](https://ffoodd.github.io/chaarts/pie-charts.html).
I strongly agree with your overall point (CSS is a real programming language that is typed, etc), but I wish you didn’t use the terms ‘weakly typed’ and ‘strongly typed’. They can be confusing and even misleading.
‘Weakly typed’ basically have two definitions:
1: A language where you can do anything to the bits that the variables point to regardless of their type. Assembler is a good example of this. You can create a string and it won’t care if you do float operations on it (though you might get weird results). This is not the case with JavaScript! When mixing types JavaScript will either automatically convert them or give you an error.
2: A language where the types of the language can be set dynamically during runtime. This means that every time you set a variable to something it can change type. The variables still have a type and they often check the type during runtime! If you mix types the language might automatically convert it in some cases or throw an error. This is what JavaScript does.
Better, more clear, less confusing and less contentious terminology is ‘dynamically typed’ and ‘statically typed’. In this terminology JavaScript is dynamically typed since it’s types are resolved at runtime. They still have types and it doesn’t just let you do anything with the variables regardless of types. Then with something like TypeScript is statically typed since its types are resolved at compile time.
I think by this definition CSS is statically typed, but I’m not really qualified to comment on the intricacies of CSS. Either way, I think it’s good to use definition with clear definitions. Whenever someone uses the term ‘weakly typed’ I often wonder which definition of term they are using (tho, I’m pretty sure you’re using the second one I mentioned above here).
Strong/Weak typing is orthogonal to Static/Dynamic typing. Your first definition is weak typing. Your second definition is Dynamic typing. I would consider JavaScript dynamically and weakly typed – it tries really hard to convert a value into a type sure, but it’s still doing an implicit cast. C makes for an interesting case study – it is statically typed and not strongly type; at least in regards to arithmetic. It will automatically convert between floats and ints, which can lead to unintentional rounding errors for new developers.
I will concede that strong/weak typing is not well defined, but Dynamic typing is. A dynamically typed languages does typing at runtime, and a statically typed language does typing at compile time.