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 (
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.
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 device the browser is on,
- What other work the browser is doing, and
- What other work the device’s other programs are doing.
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:
- Globally-scoped keywords:
- Custom identifies, which are specifically used for things, such as providing a
- Strings, such as,
- URLs, such as
- Dashed idents (
--), which are used to create custom properties (more on this in a bit)
- Integers, which are decimal numbers 0–9
- Real numbers, such as
- Percentages, such as
- Dimensions, a number with a unit appended to it such as (
- Ratios, such as
- flex, a variable length for CSS grid calculation
- Angles, such as
- Time, such as
- Frequencies, such
- Resolution, such as
Dimensions and lengths might seem similar, but dimensions can contain percentages and lengths cannot.
- Named colors, such as
- Named colors, such as
- RGB colors:
- Hexidecimal notation, such as
- 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
- 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 notion
linear-color-stop, a color and length expression used to indicate a gradient color stop
linear-color-hint, a length percentage used to interpolate color
ending-shape, which uses a keyword of either
ellipsefor radial gradients
2D positioning types
- A percentage length, such as
Programming in CSS
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.
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.
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
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.
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
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
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
1.5rem declared on the
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
Example 3: Redefined custom property
: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 is a valid, typed value, it is not a type that
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.
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.
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.
Thank you to Miriam Suzanne for her feedback.
You can support CSS-Tricks by being an MVP Supporter.