Recent Posts
Archives

Posts Tagged ‘DeclarativeLanguages’

PostHeaderIcon [PyConUS2026] Fall in Love with CSS: Reclaiming a Declarative Language for Modern Web Development

Lecturer

Piper Thunstrom is a long-standing participant in the Python community, a frequent conference speaker, an open-source maintainer, and a practicing web developer. She serves as one of the primary maintainers of PursuedPyBear (commonly abbreviated PPB), an education-oriented game engine implemented entirely in Python, and is co-founder of Tea House Hosting, a service designed to restore simplicity to static site hosting. Her professional trajectory includes freelance web development beginning in 2014 and later roles as a staff engineer. She regularly addresses topics that span community building, game development with Python, and practical aspects of contemporary web technologies.

Public resources associated with her work include the personal site https://piper.thunstrom.dev, a Bluesky presence, the GitHub account pathunstrom, the Tea House Hosting platform at https://teahouse.cafe, and the educational project Steal This Website at https://stealthiswebsite.art. The latter offers pre-constructed layouts under open terms so that learners may examine and adapt real examples.

Abstract

This article provides a detailed examination of Piper Thunstrom’s presentation on the conceptual foundations and recent evolution of Cascading Style Sheets. It positions CSS as a declarative language whose behavior is governed by transparent, learnable rules rather than opaque magic. The discussion covers the document model, the anatomy of rule sets, the full cascade including origin and specificity, media queries, custom properties, native nesting, modern color systems, and the two principal layout models—Flexbox and Grid. Cultural attitudes that have historically discouraged deep engagement with CSS receive critical attention, and practical patterns for responsive design without heavy frameworks are illustrated.

Understanding CSS as a Declarative System and the Mechanics of the Cascade

For many software engineers, Cascading Style Sheets occupy a peripheral status. The language is frequently treated as an unavoidable ancillary technology, the subject of jokes, and the target of successive generations of frameworks and preprocessors whose primary selling point is the reduction of direct contact with CSS itself. Thunstrom begins by rejecting that framing. She invites the audience to regard CSS as a declarative language comparable to SQL: the author states the desired visual outcome, and the browser’s rendering engine determines an efficient way to realize it. The translation rules that convert declarations into pixels are systematic and therefore capable of being mastered.

CSS operates on documents. A typical HTML document contains a hierarchy of elements. Those elements fall into two principal categories. Block-level elements generate boxes that participate in the block axis (top-to-bottom in languages that use horizontal writing systems) and ordinarily expand to fill the available inline size. Inline-level elements generate boxes that flow along the inline axis (left-to-right in English) and wrap when necessary. The most generic block element is the div; the most generic inline element is the span. Contemporary practice favors semantic elements over generic containers, yet a thorough treatment of semantic markup lies outside the scope of a single presentation.

The atomic unit of author-written CSS is the rule set. A rule set consists of a selector followed by a declaration block delimited by curly braces. Inside the block appear one or more property declarations, each formed by a property name, a colon, a value or list of values, and a terminating semicolon. Values may be simple keywords or lengths, comma-separated lists of related tokens (as in font-family), or complex shorthand values that compress several related longhand properties into a single statement. Should any individual declaration prove invalid—whether because of an unrecognized property name, an incorrect value type, or a missing semicolon that merges two properties—the browser discards only the faulty declaration and continues to apply every remaining valid declaration in the same block.

Selectors determine which elements receive the declarations. Basic selectors include type selectors that match element names, class selectors introduced by a period, and ID selectors introduced by a hash. Compound selectors combine several basic selectors without intervening combinators, thereby requiring an element to satisfy multiple conditions simultaneously. Complex selectors join simple or compound selectors with combinators; the most common combinator is the space character, which denotes the descendant relationship. Other combinators express direct-child, adjacent-sibling, and general-sibling relationships. Attribute selectors enclosed in square brackets match the presence or specific values of HTML attributes. Pseudo-classes, written with a single colon, select elements according to state or structural position. Pseudo-elements, written with a double colon, generate or target fragments that do not exist as independent elements in the document tree, such as ::before and ::after. Finally, a comma-separated list of selectors functions as a logical disjunction: the declarations apply if any of the listed selectors match. An important caveat is that an invalid selector anywhere in the list renders the entire rule set inert.

The cascade is the algorithm that resolves conflicts when multiple rule sets claim the same element. The first filter is relevance: only rule sets whose selectors actually match the element under consideration are retained. Surviving rule sets are then ordered by origin. Author styles (those written by the site developer) outrank user styles (historically supplied by the visitor for accessibility customization) which in turn outrank the user-agent styles that constitute the browser’s default presentation. Within the author origin, cascade layers may further subdivide priority, although a full treatment of layers exceeds the time available in the talk. Specificity supplies the next ordering criterion. Specificity is conventionally represented as a three-component tuple. The first component counts ID selectors, the second counts class selectors, attribute selectors, and pseudo-classes, and the third counts type selectors and pseudo-elements. Comparison proceeds component-wise from left to right; a higher value in an earlier component decides the contest regardless of later components. Combinators themselves do not increment any specificity component, yet every simple selector they connect does contribute. The !important flag inverts the normal origin order and should be employed only as a last resort; the preferred alternatives are carefully layered cascade layers and consistently low-specificity author styles that remain easy to override with modest increases in specificity.

Media queries, an older yet still essential feature, allow entire groups of rule sets to be activated or deactivated according to characteristics of the output medium or the user’s preferences. Queries may test media type (screen or print) and features such as minimum or maximum viewport width, preferred color scheme, and reduced-motion settings. The :root pseudo-class selects the document’s root element—almost always the html element—with a specificity of (0,1,0), slightly higher than a bare type selector. It therefore serves as a convenient and slightly sticky location for the declaration of default custom properties.

Modern Capabilities: Variables, Nesting, Color, and Layout

Custom properties, officially known as CSS variables, are any property whose name begins with two hyphens. Their values may be any syntactically valid CSS value, they inherit by default, and they are retrieved by means of the var() function. Because inheritance walks the document tree, a custom property declared on an ancestor is visible to all descendants unless overridden closer to the element. Placing a set of default custom properties on :root therefore establishes a global theme that any subtree may refine. The var() function may itself appear inside other functions such as calc(), permitting arithmetic composition of custom values.

Native nesting, long a feature of preprocessors, is now part of the CSS standard. An outer rule set is termed the parent; any rule sets written inside its declaration block are children. The selectors of child rules are automatically combined with the parent selector, most often by inserting a descendant combinator. The nesting selector & makes the combination explicit and permits the construction of compound selectors or the repositioning of the parent selector to the end of a complex selector. The resulting syntax remains compact while preserving the cascade’s ordinary specificity rules.

Color management has advanced markedly. The color-scheme property informs the browser which color schemes the page supports; the order of the keywords conveys author preference. When only the scheme is declared and no explicit colors are supplied, the browser may automatically adapt its own user-agent styles. The light-dark() function accepts two values and resolves to one or the other according to the active scheme, eliminating the need for a separate media query in many common cases. By storing both the light and dark values inside custom properties declared on :root and then applying those properties via the universal selector, an author can establish a coherent theme for the entire document. Re-declaring the same custom properties on a subtree instantly re-themes that portion of the page. Newer color spaces such as OKLCH and OKLab are designed so that equal numerical distances correspond more closely to equal perceptual differences. Relative color syntax further allows extraction of individual channels from an existing color and the construction of derived colors through arithmetic or replacement of those channels.

Layout is the domain in which modern CSS most dramatically reduces historical complexity. Flexbox supplies a one-dimensional model. Setting display: flex on a container causes its children to become flex items. The flex-direction property chooses the main axis (row or column, optionally reversed). justify-content distributes free space along the main axis; useful values include space-between and space-around. align-items controls alignment on the cross axis and is the reliable answer to the perennial question of vertical centering. When multiple lines of flex items appear, align-content governs the distribution of those lines. The flex-wrap property determines whether items may wrap; omitting an explicit wrap value is a frequent source of unexpected single-line compression.

Grid supplies the complementary two-dimensional model. After display: grid, the author defines the track structure with grid-template-rows and grid-template-columns. The optional grid-template-areas property assigns names to rectangular regions of the grid; those names must form contiguous, non-overlapping rectangles. Children are then placed by setting grid-area to the corresponding name. Because the visual structure is expressed declaratively in the template, responsive redesign often reduces to the redefinition of only the track sizes and the area map at successive breakpoints. A mobile-first approach leaves elements in ordinary document flow; a medium breakpoint activates Grid with a modest number of columns; a larger breakpoint merely adjusts the column tracks and rewrites the area template. The resulting style sheet remains short, readable, and free of the deep nesting of presentational wrappers that many component frameworks impose.

Thunstrom’s own preferred pattern for responsive Grid layouts illustrates the economy of the approach. The same three content regions—header, main, and footer—can be arranged as a single column on small screens, a two-column intermediate layout, and a three-column wide layout solely by changing the template definitions. No additional markup is required, and the cascade continues to operate in its ordinary fashion.

Cultural attitudes receive explicit treatment. The phrase “contempt culture,” introduced in a blog post approximately a decade earlier, describes the social practice of dismissing technologies or communities that fall outside a currently prestigious narrative. Within the Python community one occasionally hears the claim that Python is “just a learning language”; analogous dismissals have long been directed at CSS. Thunstrom regards such contempt as corrosive. CSS is a capable language whose feature set has expanded substantially over the past fifteen years. Mastery of its rules restores direct expressive power to developers who had previously felt obliged to route every visual decision through the abstractions of a framework.

Two short code examples demonstrate the practical outcome of the techniques discussed. The first shows a progressive Grid layout; each line is kept under the required character limit:

.container {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "header"
    "main"
    "footer";
}

@media (min-width: 45em) {
  .container {
    grid-template-columns:
      1fr 3fr 1fr;
    grid-template-areas:
      "header header header"
      "nav    main   aside"
      "footer footer footer";
  }
}

.header { grid-area: header; }
.main   { grid-area: main; }
.nav    { grid-area: nav; }
.aside  { grid-area: aside; }
.footer { grid-area: footer; }

The second fragment illustrates a reliable centering utility that exploits Flexbox:

.center {
  display: flex;
  justify-content: center;
  align-items: center;
}

Taken together, custom properties for theming, nesting for organization, and the two layout systems for structure constitute a lightweight, standards-based toolkit. Developers who internalize these tools regain the ability to express visual intent directly, to maintain that expression over time, and to adapt it to changing viewport sizes without the overhead of large framework abstractions.

Links: