AppSavvyBook a call
Canvas (Airdev)

Working With (and Around) the Canvas Style System

The Canvas style system is powerful but has real limits. The patterns that work, where it breaks, and how to keep design consistency on a Canvas app over years.

Will Driscoll8 min read

The Canvas style system is one of the strongest features of Airdev's framework. It is also one of the parts that most reliably trips teams up at scale. Two structural limits, mostly inherited from Bubble itself, cause most of the trouble.

This article describes how the style system works in practice, where it breaks, and the patterns we use at AppSavvy to keep design consistency across a Canvas app over years rather than months.

What the Canvas style system does well

The Canvas style system organises Bubble's styles into a coherent design system. You get:

  • Named style variables (colours, fonts, spacing) that get applied consistently across the app
  • A clear naming convention so designers and engineers speak the same language
  • Reusable element patterns that respect the style variables
  • A workflow for updating the design system across the whole app

For apps under a year old, the system mostly works the way you would expect. You can rebrand the app in an afternoon. New pages pick up the variables automatically. The team stays on the same visual language.

This is real value. Many Bubble apps without Canvas have nothing comparable and end up with hard-coded colours and fonts scattered across hundreds of elements.

Where it starts to creak

The system has two structural limits that show up as apps grow.

Limit 1: Bubble does not import style variables across apps

If you build a v2 of your app in a new Bubble app (most commonly during a Canvas v5 to v6 rebuild) you cannot import your style variables from the old app. You have to manually re-create every variable in the new app.

For a large design system with 40 colour variables, 12 fonts, and 30 spacing variables, this is hours of careful manual work. Get one variable wrong and the visual drift is hard to spot.

This is a Bubble platform limit, not a Canvas one. Airdev cannot fix it on your behalf.

Limit 2: not every Bubble element respects style variables uniformly

Some Bubble element types do not surface style variables in their style picker. Some plugin elements ignore the variables entirely. Some elements respect colour variables but not font variables, or vice versa.

The result is that even in a well-disciplined Canvas app, you will find hardcoded values scattered across plugin-provided elements, certain group containers, and some custom configurations.

The patterns that work

Despite the limits, several patterns make the style system more robust at scale.

Pattern 1: name variables by intent, not appearance

A variable called primary-blue is tied to its current value. When the brand changes, every reference needs reviewing.

A variable called brand-primary is tied to its role. When the colour changes, the role does not. References stay correct.

Naming by intent makes refactors faster and cleaner. It also makes it obvious when a colour is being misused (a variable called error-red showing up on a success state is an obvious bug).

Pattern 2: limit the palette to what you actually need

Twelve colours is plenty for most apps. Twenty is a lot. Thirty is a design system that nobody understands.

When the palette grows past what designers can hold in their head, consistency degrades. Reviews stop catching colour misuse. New pages drift.

If you are past 20 colour variables, the cleanup is to consolidate similar values back into their semantic intent.

Pattern 3: document the variables

Maintain a one-page reference doc that lists every style variable with its semantic meaning and intended use. This is the input designers, engineers, and external contributors use to make consistent decisions.

The doc is most useful if it includes examples ("Use text-muted for secondary labels in lists, but text-disabled for inputs that cannot be edited").

Pattern 4: lint for hardcoded values

Periodically (quarterly maintenance is a good cadence) search the app for hardcoded colour and font values. Every one that should be using a variable is a small inconsistency that compounds.

You can search the editor for specific hex values and look at how many elements use them. Plug-in created elements are the most common offenders.

Pattern 5: keep typography variables tightly scoped

Font choices in Bubble are particularly annoying to refactor because each text element stores its own font reference. Define a small number of typography variables (heading, body, caption, code), use them consistently, and resist the urge to introduce variations.

Pattern 6: avoid plugin elements for visual primitives

If a plugin provides a button, modal, or input element, it usually does not respect the Canvas style variables fully. Wherever possible, use Canvas's own primitives for visual elements and reserve plugins for genuinely novel functionality.

The patterns that fight you

A few patterns we see going wrong in older Canvas apps.

Anti-pattern 1: a different palette per page

Pages built at different times by different people with slightly different colour values. The app looks subtly off without anyone being able to point at why.

Fix is the lint pattern above. Schedule a half-day to walk through pages and normalise everything to the variables.

Anti-pattern 2: typography variables nobody uses

Twenty named font variables of which the app only uses three. New engineers see the long list, pick one at random, and the inconsistency grows.

Fix is to prune. Delete unused variables. Keep the active set small.

Anti-pattern 3: hardcoded values via conditional formatting

A field set to use brand-primary normally, but conditional formatting overrides it to a specific hex value for one state. The conditional formatting bypasses the variable system.

Fix is to use a different variable for the conditional state, not a hex value.

Anti-pattern 4: plugin elements imported untouched

A plugin's default styling baked into your app. Looks fine in isolation; looks alien next to your own design system.

Fix is to wrap the plugin element in a styled container, or build a thin reusable wrapper that applies your variables.

When the style system is not enough

For sufficiently large apps, the style variable system runs out of expressiveness. You need:

  • Computed styles (this colour, but lighter when the user prefers dark mode)
  • Token cascades (semantic tokens that reference primitive tokens)
  • Programmatic theming (the same UI rendered in multiple brands)

Bubble cannot really do these. If your app has reached this point, either:

  • Accept that styling will involve manual updates for any change, OR
  • Plan a migration to a code stack where modern CSS gives you all of this natively

The first option is fine for apps that have stabilised their visual language. The second is the right answer for apps where the design system is still evolving.

What to do next

If you would like an external review of your style system and design consistency, request a free Bubble app audit - the Maintainability section flags style drift as part of the report.

If you want a senior pair of eyes on a style refactor, book a 30-minute discovery call.

Read next: Canvas reusable elements best practices and The Canvas tech debt audit.

Got a Bubble or Canvas app you’d like a second pair of eyes on?

30-minute discovery call. We’ll look at your app live and tell you honestly what we’d do next.

Or grab the Bubble migration playbook PDF.