Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Archives
Log in
June 16, 2026

Validating Style

In this issue:

  • Planning it out
  • Try it, you'll like it
  • Creating ANSI
    • Adding Decorations
  • Format-SpectreStyleHashTable
  • Format-PSStyleCSV
  • Summary

Last time, I shared my experience in creating a tool that used the pwshSpectreConsole module to display a CSV file in style. The function has a defined "stylesheet" in the form of a hashtable of SpectreConsole styles.

@{
    Delimiter = 'Aqua'
    Quotes    = 'CornflowerBlue'
    Header    = 'Gold1 Italic Bold'
    Number    = 'LightGoldenrod1'
    DateTime  = 'MediumOrchid1'
    True      = 'Chartreuse1'
    False     = 'LightCoral'
    Text      = 'LightSalmon1'
    Comment   = 'Green1 underline'
}

However, the function included a parameter to allow the user to specify their own hashtable. Or they can supply a partial hashtable and the function will merge the style settings.

As I was working on the function, I realized it would be nice to see the hashtable formatted using the specified style. In other words, show Aqua using the Aqua SpectreConsole color.

Let me take a few minutes and walk you through how I created Format-SpectreStyleHashtable.

Obviously, I need to pass a hashtable as a parameter.

param(
    [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
    [ValidateNotNullOrEmpty()]
    [hashtable]$Style
)

This function could be used with a hashtable of style values you want to use with Format-SpectreJson or my CSV function, so I am using a more generic parameter name. The name also matches the alias of the CsvStyleHash parameter in my function.

Planning it out

Whenever building something in PowerShell, I recommend outlining the steps you want to take. Start with the major steps. You might even start with the last step. For me it was:

# write a custom object showing style text using the style

In other words, I wanted the property value to be displayed in the corresponding style. This means the property will have to be formatted text. The best way to achieve this result is to use ANSI escape sequences like we use in $PSStyle. Recognizing this step, I can work backwards and fill in other steps.

By talking my way through the process I end up with a list of steps.

# create a temporary hashtable
# enumerate the style hashtable keys and values
# convert the SpectreConsole style to ANSI
# parse the value as [Spectre.Console.Style]
# convert decorations like italic to ANSI equivalent
# add the ANSI style to the temporary hashtable
# write a custom object showing style text using the style

All I need to do is write PowerShell code to fulfill these steps. The comments also serve as documentation when I am finished. Although there's nothing wrong with deleting an item if other parts of your code make it clear what is happening.

Based on my plan, I need to define a temporary hashtable.

# create a temporary ordered hashtable
# the user might expect to see keys in a particular order
$clone = [ordered]@{}

This prevents me from accidentally changing anything in the original hashtable.

I also know that in order to process each key/value pair, I need to enumerate the hashtable.

$Style.GetEnumerator() | ForEach-Object {
    #process each key and value
    #$_.key e.g. Delimiter
    #$_.value e.g. Aqua
}

Try it, you'll like it

The crux of the function is to take a string like Aqua and first validate that it is a SpectreConsole color. And if it is, convert that color to an ANSI sequence. Actually, I need to take this a step further because I could have a value like Aqua italic. To process this, I need to use the Spectre.Console.Style class. Using my handy Get-TypeMember command, I can inspect this class.

PS C:\> Get-TypeMember Spectre.console.Style

   Type: Spectre.Console.Style

Name           MemberType ResultType IsStatic IsEnum
----           ---------- ---------- -------- ------
Combine        Method     Style
GetType        Method     Type
Parse          Method     Style          True
ToMarkup       Method     String
TryParse       Method     Boolean        True
WithBackground Method     Style          True
WithDecoration Method     Style          True
WithForeground Method     Style          True
WithLink       Method     Style          True
Background     Property   Color
Decoration     Property   Decoration            True
Foreground     Property   Color
Link           Property   String
Plain          Property   Style

To validate the style, I could try to create an instance of the object. But I see a few parsing methods that might be better choices. You'll see similar methods on other objects in the .NET Framework.

The Parse method, at least in this instance, will attempt to use the value to create a style object.

PS C:\> [Spectre.Console.Style]::parse("Aqua")

Foreground Background Decoration Link
---------- ---------- ---------- ----
aqua       default          None

An invalid color will throw an exception.

PS C:\> [Spectre.Console.Style]::parse("Aqua2")
MethodInvocationException: Exception calling "Parse" with "1" argument(s): "Could not find color or style 'Aqua2'."

I could use this in my code and invoke the static method.

Try {
    $s = [Spectre.Console.Style]::Parse("Aqua2 italic")
}
catch {
   Write-Warning "Invalid color or style"
}

The alternative is the TryParse() method.

PS C:\> [Spectre.Console.Style]::TryParse.OverloadDefinitions
static bool TryParse(string text, [ref] Spectre.Console.Style result)

This gives me a boolean result so I can use and If/Else statement. But let's look at this method because it is using something you will occasionally come across.

The method takes two parameters. The first is the text I want to try as a style. The second parameter is the result. If the method succeeds, the output will be store in this variable.

PS C:\> [Spectre.Console.Style]::TryParse("Aqua italic",$result)
MethodException: Argument: '2' should be a System.Management.Automation.PSReference. Use [ref].
Want to read the full issue?
Already a paid subscriber? Click here to log in.
GitHub
Bluesky
LinkedIn
Mastodon
jdhitsolutions.github.io
Powered by Buttondown, the easiest way to start and grow your newsletter.