Case study · Cloud infrastructure

OpenNebula

I rebuilt 21 resource views that each behaved differently on one shared pattern, and checked every one against OpenNebula's own documentation before it shipped.

RoleProduct Designer
Year2026
PlatformWeb
ScopeNavigation + 21 resource views
OpenNebula Sunstone in a browser window on a pastel gradient, with the Images list next to an open virtual machine drawer showing its details, permissions and ownership
12Usability sessions and stakeholder interviews
−34%Usability-related tickets after release
6Functional prototypes built in HTML/CSS/JS
21Resource types, one shared pattern
Overview

An open-source console, redesigned resource by resource.

OpenNebula is open-source software for building and running private, public and edge clouds. Admins manage data centers, virtual machines, storage and networking from one web console called Sunstone. It can do a lot, it puts a lot on every screen, and the interface was starting to show its age.

The redesign covers Sunstone 7.0's navigation and resource views: 21 resource types, from Virtual Machines and Templates down to Marketplaces and ACLs, each rebuilt on the same card, table and detail pattern instead of the layout every resource had grown on its own in the current release.

Sunstone VM list, card view, dark theme
Fig. 01: The OneLLM usage and revenue dashboard, showing token usage and estimated revenue by tenant and AI service. It's built from the same Sunstone design system components.
Problem

Twenty-one resources, no shared pattern.

Over the years every resource type in Sunstone had ended up with its own list layout, its own way of opening details, and its own interpretation of what the documentation recommends.

Auditing the shipped UI against OpenNebula's own 7.0 docs surfaced real mismatches: VM Groups lived under Templates when the docs treat them as an operational concept under Instances; Marketplace Apps were buried in Storage when user intent points to their own section; VNet Templates sat in Networks instead of Templates. The detail panel was a bigger problem. In card view it opened on the right and in table view it opened at the bottom of the screen, so switching views halfway through a task meant looking for the same information in a different place.

Process

I started with the documentation, then prototyped the panel.

Before designing any screens, I mapped every resource against the official OpenNebula 7.0 documentation. That settled what each card should show, what goes into the metric tiles and which tabs each detail view needs. The question that took the most rounds was where the detail panel should open.

V1 kept the old split (right panel for cards, bottom sheet for tables), and a stakeholder review flagged the inconsistency straight away. V2 tried a right panel everywhere with real data. It turned out the right panel worked better for tables too, because it hides a few columns instead of a few rows, and people read tables from top to bottom anyway. V3, the current build, slides the panel in from the right in both views and dims the list behind it slightly so you keep your place.

Resource visualisation grid mapping all 21 resource types to card fields, metric tiles and tabs, based on the OpenNebula 7.0 documentation
Every resource mapped against the OpenNebula 7.0 documentation before any screen was drawn: what the card shows, which metric tiles it needs, which tabs the detail view gets.
Three versions of the detail panel: V1 split by view mode, V2 always on the right, V3 sliding in over a dimmed list
Three attempts at the same question. A stakeholder review turned down V1, which kept the old split. V2 used a docked right panel everywhere. V3 slides the panel in over a dimmed list, and that is the version that shipped.
Solution

The same patterns across the whole console.

VM detail drawer on the Info tab, with details, permissions and capacity panels
VM detail drawer on the Logs tab, with a searchable scheduler log
Create-user wizard, step 1 of 3, the general credentials form
Multi-select summary with five VMs selected, their combined stats and per-row actions
VM DETAIL

One drawer for every resource

All 21 resource types now open their details in the same panel on the right, so what you learn on a VM also works on an ACL rule.

MONITORING

Logs without losing context

Monitoring and log tabs sit inside the same drawer, so checking a VM's history never means leaving the list behind.

ONBOARDING

Long forms split into steps

Admin flows like creating a user used to be one long form. Now they come in steps: general info first, then permissions, then groups.

BULK ACTIONS

Act on many at once

When you select several resources, a summary bar shows the actions that apply to all of them, so you don't have to repeat the same action row by row.

Design system

Tokens that hold up in both themes.

Every surface, border and status colour is a token mapped from the Figma variables. I checked each one in dark and light mode side by side, instead of designing in dark and guessing the light values afterwards. Type sticks to a small scale, 12 to 24px at weights 400 to 600, so nav items, table cells and page titles look the same across all 21 resources.

Status always uses a colour, a label and an icon together, never colour on its own. It's the same rule I used for Labels and Tags. You should be able to read a VM's state from a screenshot, if you're colourblind, or on a bad projector in a meeting room.

The Sunstone Overview dashboard in dark mode, with VM, host, network and image counters, CPU and memory charts, and host allocation meters.
The same Sunstone Overview dashboard in light mode, token for token.
This is one screen, built once from one set of tokens. Drag the handle to move between the dark and light themes. Surfaces, borders, chart fills and status colours each have their own value per theme, so the light version got the same care as the dark one.
THE SYSTEM UNDERNEATH

One name, two values.

Nobody copies these tokens from Figma by hand. A script generates them from the Sunstone source code, following the same three layers the product uses: primitives, then aliases, then a role for each colour scheme. 173 names, 98 of them colour roles, each resolving once for light and once for dark. Names map onto the palette, so --surface-action greps straight back to schemes/light/surface.js and a developer can find where any value came from.

Token Light Dark
--surface-page #FFFFFFneutral.white #222431neutral.950
--surface-primary #FFFFFFneutral.white #2a2d3dneutral.925
--text-headings #40435cneutral.900 #f5f7f9neutral.100
--text-body #637381neutral.default #c4cdd5neutral.300
--border-primary #e0e4e7neutral.200 #40435cneutral.900
--surface-action #007099primary.default #58d2ffprimary.500
--text-on-action #FFFFFFneutral.white #003447primary.900
--text-error #ff5779error.500 #ff5779error.500, unchanged

#40435c is text/headings in light and border/primary in dark. Both themes share one colour ramp but assign it to different roles. A hex value on its own can't tell you that, which is why these tokens are named after what they do and not after their colour.

WHAT THE SIDE-BY-SIDE AUDIT FOUND

Three pairs fail contrast, and I left them in on purpose.

Putting the two themes side by side turned up contrast problems that a review of one theme would have missed: each of these passes easily in one theme and fails in the other. I measured them, wrote them up and took them to the design system team rather than quietly picking a better colour. If my prototype fixed the system without saying so, it would no longer tell anyone the truth about the system.

Selected item
1.03:1
icon/selected on surface/primary, dark

The icon on a selected sidebar row. At 1.03:1 you effectively can't see it: #003447 sitting on #2a2d3d. Passes at 5.56:1 in light.

Failed Deploy error
2.65:1
text/error on surface/error, light

Every error tag in the console. text/error is the one colour role in the table above that does not resolve twice. It is the same #ff5779 in both schemes, which is fine on the dark #290008 (6.28:1) and fails on the light wash.

Virtual Machines
3.26:1
text/focus on surface/focus2, light

The selected sidebar item, the selected table row, the current page. It only passes for large text, and this pair appears on every screen in the product. 6.22:1 in dark.

Ratios computed from the generated token values (WCAG 2.1). Four more accent pairs measured between 2.5 and 3.6:1; these three are the ones no screen can avoid.
Retrospective

Make twenty-one resource types behave like one product.

What I took from it

The docs were my best research source.

OpenNebula's own documentation settled what each resource means to an admin, which gave me a fixed reference to audit the shipped UI against, one resource at a time.

Small decisions get big when they repeat.

Right panel or bottom sheet sounded like a detail until it touched all 21 resources. Worth testing with real data before committing.

Consistency was the feature.

No single screen needed to be clever. The win was making all 21 feel like the same product.

Some questions are still open, on purpose.

Virtual Routers and VDCs still need a home. I logged them rather than guess.

Public debut

Presented live in the official OpenNebula 7.4 webinar

Before the 7.4 release, OpenNebula showed the new Sunstone in a public webinar, and I walked the community through the design process behind it: the audit of all 21 resource views, the navigation decisions, and the design system that ties it all together.

A First Look at the New Sunstone GUI in OpenNebula 7.4
OpenNebula’s official channel
starts 18:07 / 1:02:01
Live walkthrough of the design decisions behind the new Sunstone, presented alongside the OpenNebula engineering team. Playback starts at the Process chapter (18:07).