Styling Discourse with variables: A case for simpler semantics

Continuing the discussion from Styling Discourse with variables: Show & Tell:

I totally appreciate the effort to improve Discourse’s theming experience! However, I’m not entirely convinced that the approach of adding a lot of css variables as shared in the above topic is the optimal solution. I wanted to share a few thoughts on why.

I’ve been experimenting with this approach myself through the Canvas Theme Template, which essentially provides a collection of adjustable variables for building base themes:

:root {
  /* Layout */
  --d-max-width: 1110px;
  --canvas-nav-space: 0.75rem;
  --canvas-content-padding: 1.5rem;
  --canvas-topic-list-padding: 0.8em;
  
  /* Base Styles */
  --canvas-background: var(--secondary);
  --canvas-surface: var(--secondary);
  --canvas-border: 1px solid var(--primary-500);
  --canvas-border-light: 1px solid var(--primary-200);
  
  /* Border Radius */
  --d-border-radius: 2px;
  --d-border-radius-large: 2px;
  --d-button-border-radius: 2px;
  --d-input-border-radius: var(--d-button-border-radius);
  --d-nav-pill-border-radius: var(--d-button-border-radius);
  
  /* Button Styles */
  --canvas-button-padding: 0.5em 0.65em;
  --canvas-button-primary-padding: 0.5em 0.65em;
  
  /* Header */
  --canvas-header-height: 4rem;
  --canvas-header-background: var(--header_background);
  --canvas-header-border: none;
  --canvas-header-shadow: var(--shadow-header);
  
  /* Sidebar */
  --d-sidebar-width: 17em;
  --d-sidebar-background: var(--secondary);
  --canvas-sidebar-border: 1px solid var(--primary-low);
  --canvas-sidebar-scrollbar: var(--scrollbarWidth);
  --d-sidebar-row-height: 2.2em;
  --d-sidebar-highlight-background: var(--primary-low);

  /* And several more... */
}

While this works reasonably well for simple adjustments, I’ve run into several limitations when trying to scale this approach:

Cognitive Overhead and Discoverability

An extensive list of variables essentially requires a lookup table. This feels disconnected from how you’d typically work in a component-based framework, where I’d actually expect to style components. And maybe that’s just me, but I feel it shifts the mental model from “I want to style this component” to “I need to find the right variable name.”

Lack of Cascading Logic

The current implementation assigns hard-coded values directly to variables without establishing a proper cascade hierarchy:

--d-sidebar-link-color: var(--primary-high);
--d-nav-background-color--active: transparent;
--table-border-width: 1px;

This means there’s no inheritance from more general variables like --link-color or --border-width. If I want to make systematic changes, I need to update multiple specific variables instead of changing one foundational value.

Design-to-Development Gap

I think the approach creates friction when working between design tools (like Figma) and implementation. Design systems typically use semantic variables that don’t map one-to-one with these very implementation-specific variables.

An alternative approach that embraces the component architecture

I think we could achieve the same goal more naturally by combining basic semantic variables with reliable component targeting. Instead of having a long list of specific variables, what if we could just count on unique and consitstently named component classes throughout Discourse? Things like .d-sidebar, .d-topic-list, .d-header.

Then pair that with a smaller set of foundational variables that actually cascade the way CSS is meant to work:

/* Set the design foundation */
:root {
  --d-border-width: 2px;
  --d-surface-color: #3498db;
  --d-space-1: 1rem;
}

/* Override at the component level when you need to */
.d-topic-list,
.d-sidebar {
  --d-border-width: 1px;
}

To me this feels more like how CSS naturally works. I set my global styles, then refine them where needed. When I want to change how borders look across the app, I change one variable. When I want the sidebar to be different, I target the sidebar specifically.

2 Likes

Well, I think how CSS would naturally work is just skip the variables and do

/* Set the design foundation */
body {
  border-width: 2px;
  background-color: #3498db;
  margin: 1rem;
}

/* Override at the component level when you need to */
.d-topic-list,
.d-sidebar {
  border-width: 1px;
}

Maybe I’m just old.

The real problem is this

Example: simply targeting .btn or button:

.btn {
    border: 1px solid red;
}

misses the post buttons but does target the “views” link and hamburger menu.

1 Like

There are sound reasons to use variables, maybe let’s not get into them here. It’s a good point though to not argue about the nature of CSS. I should have phrased this better: it’s not about the nature of it, it’s about best practices for styling a component-based framework. And I completely agree that buttons are another good example of how these can’t be properly applied.

Looking at the bigger picture, there’s been a concerted effort to modernize the JavaScript side of the frontend framework. And I think it’s been a resounding success. Working with clean standards and well-structured classes is genuinely enjoyable now. For me as a designer, it also opened up opportunities to build new frontend components easier and more efficiently.

However, I can’t shake the feeling that there’s no similar commitment to bringing the design system up to the same standards. While adding CSS variables for every aspect is certainly more performant and cleaner than the current approach, it still feels like avoiding the deeper architectural issues: a codebase full of overly specific declarations and no clear component-scoped styles. This feels like an “easier” solution that avoids the harder problem: fully aligning the styling architecture with the framework’s modular design.

I understand that this would incur a lot of work and backward compatibility issues. But the team has tackled these challenges successfully on the JavaScript side. If JavaScript continues to receive significantly more resources than styles, that disparity will show up in the final designs. And users will feel the difference, even if they can’t articulate why.

I’d just love to see the same modernization energy applied to the CSS architecture because I’m convinced the long-term benefits for both developer and user experience would be transformative.

5 Likes