[M3] - A shift from roles to properties?

I have been spending some time testing out the Material 3 theme and noticed that there are no roles.

Things like filled/tonal/outlined are properties of components rather than roles.

This seems like a shift from using roles to directly adding/removing CSS classes. Example for a button:

@anvil_prop
  def appearance(self, value):
    button = self.dom_nodes['anvil-m3-button']
    button.classList.remove('anvil-m3-elevated')
    button.classList.remove('anvil-m3-filled')
    button.classList.remove('anvil-m3-tonal')
    button.classList.remove('anvil-m3-outlined')
    if value and value != 'text':
      button.classList.add(f"anvil-m3-{value}")

I’m curious to learn more about the reason behind this as I have a few custom components I use regularly.

2 Likes

In the end, a role is also a CSS property. I think the simple reason behind why they did it is to ensure that it does not conflict with any existing roles that you created (Since a lot of them are generic names)

3 Likes

Time to get into the Anvil Time Machine! How did Anvil themes end up the way they are?

The origin of roles

“Roles” were originally a huge hack. Time was, the only components you could really use in the designer were the built-in Anvil components. But we wanted to implement theming in Anvil – for example, we wanted an M3 theme that could include “cards” as a UI element in the toolbox. So we took the shortest route to a solution: We let one built-in Anvil component class perform different roles – for example, a ColumnPanel could just be a ColumnPanel, or it could be a card, with padding and a drop shadow. A label could be a regular label, or it could be an M3 “heading” with design-system-appropriate font, size, spacing, etc. A theme just needed to define a set of “roles” to be offered in the designer (optionally restricted by component), provide CSS implementations, and nominate some to appear in the toolbox.

This approach severely limited what a theme could do (CSS on a fixed DOM structure, based on one configuration value), but it made it possible to implement different visual looks-and-feels in Anvil.

The Problem with Roles

But this was well short of our goal. We wanted developers to be able to use any design system with Anvil apps. And for that, we knew CSS on its own would not be sufficient. Every UI design system has a different idea of what components exist, and how to parameterise them. (For example, take any two UI design systems and compare what structure, parameters and options they think should be available on a text input field.) What’s more, design systems often come with a reference implementation that provides/demands a particular DOM structure, and that doesn’t match Anvil’s built-in components at all.

So if we wanted faithful implementations of, say, an M3 text field and a GOV.UK text input, they would need to have different properties, different behaviour, different DOM structure…they’d need to be two entirely different components!

A New Approach to Themes

So, if we want anyone to be able to define a theme, and themes need to have their own components with their own properties and behaviours…the obvious approach is to make themes into Anvil dependencies that provide custom components. We added some APIs to make it easier to implement custom components with HTML and CSS.

However, to make this work, we’d need those custom components to be “live” in the designer - we need them to respond to changes in their properties and exhibit behaviour. So we need these custom components to run in the designer. Previously, the designer’s drag-and-drop system was tightly integrated into our built-in components, with a messy tightly-coupled Javascript API. This obviously wouldn’t do, so we needed a complete rewrite of the Anvil Designer, to move all all the interaction betweeen components and Anvil to a cleaner API that we could open to everyone. (Watch this space - it’s coming! If you look at the M3 theme source code, you can see glimpses of it :wink: )

One more thing…

While we were rebuilding the designer, we knew we also wanted to tackle an enduring bugbear of the drag-and-drop designer: Re-using page structure. In the old world, the only way that a theme could provide page structure was by providing an HTML template. If you wanted something as simple as a re-usable nav bar, you had to go through all sorts of hoops – typically you’d define your main form with the nav bar, then write logic to swap out the contents of its content_panel with another form based on a Blank Panel. This meant that at no point could you look at your designer and see what your whole page would look like. It was also pretty inflexible - it made it annoying, for example, to customise page titles or nav-bar entries based on which page you were on.

So we built Layouts into the new designer. This allows you to have rich, re-usable page structure that can be filled out in several places (slots). You can build them out of many components (not just one HTML document). They are live in the designer, and have properties of their own. You can build up layouts for your app in layers (eg “base M3 nav-rail layout” → “my page structure with nav-bar” → “my page structure with nav bar and user information for logged-in users” → my actual form). And of course, themes can now provide layouts.

And there you have it.

Themes are now dependencies with custom components. The new designer allows you to interact with custom components live in the designer, and also supports layouts.

If you’ve got a sense of an avalanche of feature releases, you’re not wrong. We’ve been working toward this vision for all of 2024 and a good chunk of 2023, and developing all of these things in parallel. When we say the new M3 theme is more than just a theme, this is what we mean – it’s the culmination of an awful lot of work.

And by the end of all that, we’ve removed all the original reasons for roles! There might still be some value in being able to apply a bit of extra CSS to your theme, and we’re looking at ways to do it, but the role property was always a hack around a limitation of Anvil that no longer exists.

11 Likes

This sounds like the beginnings of a “theory of operation” or “principles of design” for Anvil’s upcoming theme-based work. Great work in setting the stage! Keep it coming!

Thanks for the backstory, really interesting and puts things into perspective!

Which, presumably, makes jsx within an anvil app a possibility…