27 August 2022
Designing the kbupdate Dashboard Components with PowerShell Universal
Recently I was contacted by Chrissy LeMaire, PowerShell enthusiast, to assist with designing a PowerShell Universal theme for her project, kbupdate.
I was sent a sample of "CSS" written in a PowerShell hashtable and was told that somehow this would magically be turned into the CSS that will control the look and feel of the website.
Let's get a few things clear here, I know ZERO PowerShell. So this concept was a bit odd to me at first, but also totally awesome. Even though I've never really worked with PowerShell, I decided to give the project a go. I'm always up for a good challenge and learning something new.
The prerequisites:
Diving In
The first thing I did was scavenged the PowerShell Universal docs for a few hours to try to wrap my brain around how PowerShell Universal worked. Turns out it is built on top of the Material UI framework, specifically the default theme. So not only does PowerShell Universal convert PowerShell to CSS, but it also converts PowerShell into React components. How cool!
Choosing a styling approach
In PowerShell Universal, there is the option to add a traditional custom CSS file in addition to the theme.ps1
(the intimidating hashtable from earlier) file. Initially, I was excited about this, however, the more I tested it out, it made more sense to embrace PowerShell and use the CSS file as little as possible.
Why? Updating styles in the theme.ps1
file tends to override rules more gracefully than a CSS file. Updating the theme.ps1
file adds extra styles onto the initial rule supplied by Material UI instead of creating a new rule entirely. This removes the need for a bunch of unnecessary !important
flags floating around, so for me, this was the obvious choice.
Breaking down the theme.ps1 file
The theme.ps1
file turned out to not be so scary after all. There are two different areas for adding specific styles for dark and light modes. These are then compiled into a dark.css
and a light.css
and applied whenever a user switches their theme mode.
For these examples, we'll target the top navigation bar with the class .MuiAppBar-root
.
@{
light = @{
palette = @{
mode = "light"
background = @{
default = "#f6f8fa"
}
primary = @{
main = "#209fda"
}
}
overrides = @{
MuiAppBar = @{
root = @{
backgroundColor = "#ffffff"
backgroundImage = "none"
borderBottom = "1px solid #dddfe1"
}
}
}
}
dark = @{
palette = @{
mode = "dark"
background = @{
default = "#0d1117"
}
primary = @{
main = "#209fda"
}
}
overrides = @{
MuiAppBar = @{
root = @{
backgroundColor = "#161b22"
backgroundImage = "none"
borderBottom = "1px solid #30363d"
}
}
}
}
}
/* light.css */
.MuiAppBar-root {
/* MUI styles */
/* Custom styles below */
background-color: #ffffff;
background-image: none;
border-bottom: 1px solid #dddfe1;
}
/* dark.css */
.MuiAppBar-root {
/* MUI styles */
/* Custom styles below */
background-color: #161b22;
background-image: none;
border-bottom: 1px solid #30363d;
}
As you can see above, the conversion from PowerShell to CSS is quite simple. Just by comparing the two, you can pretty much tell what is going on. The PowerShell Universal docs are also very good at explaining this and giving a few examples of what is possible.
Cool. So I modified a few things, and saw the changes, everything was looking good. Along with the main background colors, the alert component was the first thing I styled.
One problem, my theme.ps1
file was becoming a million miles long. I was duplicating a lot of the same code in both themes when I really only needed to change the value and not the rule itself. This was getting increasingly more annoying, so, I did a thing:
$colors = Get-Colors
$common = $colors | Where-Object Mode -eq common
$themes = $colors | Where-Object Enabled
$allThemes = @{}
ForEach ($theme in $themes) {
$allThemes += @{
$theme.Mode = @{
palette = @{
mode = $theme.Mode
background = @{
default = $theme.Main
}
primary = @{
main = $common.Blue
}
}
overrides = @{
MuiAppBar = @{
root = @{
backgroundColor = $theme.MainSecondary
backgroundImage = "none"
borderBottom = "1px solid " + $theme.MainGamma
}
}
}
}
}
}
[
{
"Mode": "common",
"Enabled": false,
"Blue": "#209fda"
},
{
"Mode": "dark",
"Enabled": true,
"Main": "#0d1117",
"MainSecondary": "#161b22",
"MainGamma": "#30363d"
},
{
"Mode": "light",
"Enabled": true,
"Main": "#f6f8fa",
"MainSecondary": "#ffffff",
"MainGamma": "#dddfe1"
}
]
function Get-Colors {
Get-Content -Path (Join-Path -Path $script:UDRoot -ChildPath colors.json) | ConvertFrom-Json
}
Boom!š„ Now, instead of repeating the same thing in multiple places, we define it once and loop through the colors.json
file to pull the color values. This beautiful piece of PowerShell cuts down the theme.ps1
file in half, plus all the colors are contained in one spot. You can also use the colors on components just by referencing them on the page file with the Get-Colors
function.
# dashboard.ps1
$colors = Get-Colors
$common = $colors | Where-Object Mode -eq common
New-UDCheckBox -Icon $Icon -CheckedIcon $CheckedIcon -Style @{
color = $common.Blue
}
The Results
After a few weeks of hackery (this would have been faster, but kids), I came up with some pretty sweet concepts. I put together a sample dashboard to show off some of the components.
Wait, hold up. Is that... dim mode?
Turns out, since all the colors are contained in the JSON file, adding or updating a theme is a breeze. Just swap out a few colors and that's it! Pretty nifty, aye?
Let's go back to the prerequisites:
Final thoughts
Overall, I had a great time working on this project and definitely learned more about PowerShell. It was a nice break away from my normal go-to technologies, and working with Chrissy has been fun!
Things to note:
- You don't need to know PowerShell to write a theme for PowerShell Universal.
- Even though PowerShell Universal converts PowerShell into React components, you do not need to know React either.
- You also don't need to know much about the default Material UI theme, but reading their docs does help to create a basic understanding.
- PowerShell Universal is actively maintained and adding new features and bug fixes on a regular cadence.
Will I do another project in PowerShell Universal? Perhaps. I'd like to try my hand at creating some custom components as well. I have ideas. š¤āļøš¬
Have more questions or just want to chat? Connect with me below. I love hearing and learning from other rad developers!