Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions scripts/spo-apply-site-theme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

## Summary

Apply a theme to a specific SharePoint site without adding a tenant theme that would be available for all sites through the "Change the look" menu.
Apply a theme to a specific SharePoint site without adding a tenant theme that would be available for all sites through the "Change the look" menu. This sample is available in both PnP PowerShell and CLI for Microsoft 365.

![Example Screenshot](assets/preview.png)
Example of applying a custom theme to a SharePoint site using CLI for Microsoft 365:

![Example Screenshot of CLI for Microsoft 365](assets/preview.png)


# [PnP PowerShell](#tab/pnpps)
Expand Down Expand Up @@ -71,6 +73,86 @@ Disconnect-PnPOnline
```
[!INCLUDE [More about PnP PowerShell](../../docfx/includes/MORE-PNPPS.md)]

# [CLI for Microsoft 365](#tab/cli-m365-ps)

```powershell
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory, HelpMessage = "SharePoint site URL to apply the theme")]
[string]$SiteUrl,

[Parameter(Mandatory, HelpMessage = "Path to theme JSON file or inline JSON string")]
[string]$ThemeJson,

[Parameter(HelpMessage = "Unique name for the temporary theme")]
[string]$ThemeName = "TempTheme-$(Get-Date -Format 'yyyyMMddHHmmss')",

[Parameter(HelpMessage = "Keep the theme registered in tenant after applying")]
[switch]$KeepTheme
)

begin {
m365 login --ensure
if ($LASTEXITCODE -ne 0) {
throw "Failed to verify login status"
}

$themeContent = if ($ThemeJson.StartsWith('@')) {
$filePath = $ThemeJson.Substring(1)
if (-not (Test-Path $filePath)) {
throw "Theme file not found: $filePath"
}
Get-Content $filePath -Raw
} else {
$ThemeJson
}
}

process {
if (-not $PSCmdlet.ShouldProcess($SiteUrl, "Apply theme '$ThemeName'")) {
return
}

Write-Host "Registering theme '$ThemeName'..." -ForegroundColor Cyan
$setResult = m365 spo theme set --name $ThemeName --theme $themeContent 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to register theme: $setResult"
}

Write-Host "Applying theme to: $SiteUrl" -ForegroundColor Cyan
$applyResult = m365 spo theme apply --name $ThemeName --webUrl $SiteUrl --output json 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to apply theme: $applyResult"
}

Write-Host "Theme applied successfully!" -ForegroundColor Green
Write-Host "Catalog path: $applyResult" -ForegroundColor Gray

if (-not $KeepTheme) {
$removeResult = m365 spo theme remove --name $ThemeName --force 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Warning "Failed to remove temporary theme. You may need to remove it manually."
}
} else {
Write-Host "Theme kept in tenant gallery" -ForegroundColor Yellow
}
}

# Example 1: Apply theme from JSON file
# .\Apply-SiteTheme.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/ProjectX" -ThemeJson "@C:\themes\custom-theme.json"

# Example 2: Apply theme from inline JSON and keep it in tenant
# .\Apply-SiteTheme.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/ProjectX" -ThemeJson '{"themePrimary":"#d81e05","themeLighterAlt":"#fdf5f4","themeLighter":"#f9d6d2","themeLight":"#f4b4ac","themeTertiary":"#e87060","themeSecondary":"#dd351e","themeDarkAlt":"#c31a04","themeDark":"#a51603","themeDarker":"#791002","neutralLighterAlt":"#eeeeee","neutralLighter":"#f5f5f5","neutralLight":"#e1e1e1","neutralQuaternaryAlt":"#d1d1d1","neutralQuaternary":"#c8c8c8","neutralTertiaryAlt":"#c0c0c0","neutralTertiary":"#c2c2c2","neutralSecondary":"#858585","neutralPrimaryAlt":"#4b4b4b","neutralPrimary":"#333333","neutralDark":"#272727","black":"#1d1d1d","white":"#f5f5f5"}' -KeepTheme

# Example 3: Test with WhatIf before applying
# .\Apply-SiteTheme.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/ProjectX" -ThemeJson "@theme.json" -WhatIf

# Example 4: Apply with verbose output
# .\Apply-SiteTheme.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/ProjectX" -ThemeJson "@theme.json" -Verbose

```

[!INCLUDE [More about CLI for Microsoft 365](../../docfx/includes/MORE-CLIM365.md)]

***

Expand All @@ -79,6 +161,7 @@ Disconnect-PnPOnline
| Author(s) |
|-----------|
| [Aimery Thomas](https://github.com/a1mery)|
| Adam Wójcik [@Adam-it](https://github.com/Adam-it)|

[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)]
<img src="https://m365-visitor-stats.azurewebsites.net/script-samples/scripts/spo-apply-site-theme" aria-hidden="true" />
Binary file modified scripts/spo-apply-site-theme/assets/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified scripts/spo-apply-site-theme/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions scripts/spo-apply-site-theme/assets/sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
"Apply a theme to a specific SharePoint site without adding a tenant theme that would be available for all sites through the 'Change the look' menu."
],
"creationDateTime": "2025-10-22",
"updateDateTime": "2025-10-22",
"updateDateTime": "2026-01-10",
"products": [
"SharePoint"
],
"metadata": [
{
"key": "PNP-POWERSHELL",
"value": "3.1.0"
},
{
"key": "CLI-FOR-MICROSOFT365",
"value": "11.3.0"
}
],
"categories": [
Expand All @@ -26,7 +30,11 @@
"Connect-PnPOnline",
"Get-PnPAccessToken",
"Invoke-RestMethod",
"Disconnect-PnPOnline"
"Disconnect-PnPOnline",
"m365 login",
"m365 spo theme set",
"m365 spo theme apply",
"m365 spo theme remove"
],
"thumbnails": [
{
Expand All @@ -41,13 +49,23 @@
"gitHubAccount": "a1mery",
"pictureUrl": "https://github.com/a1mery.png",
"name": "Aimery Thomas"
},
{
"gitHubAccount": "Adam-it",
"pictureUrl": "https://avatars.githubusercontent.com/u/58668583?v=4",
"name": "Adam Wójcik"
}
],
"references": [
{
"name": "Want to learn more about PnP PowerShell and the cmdlets",
"description": "Check out the PnP PowerShell site to get started and for the reference to the cmdlets.",
"url": "https://aka.ms/pnp/powershell"
},
{
"name": "Want to learn more about CLI for Microsoft 365 and the commands",
"description": "Check out the CLI for Microsoft 365 site to get started and for the reference to the commands.",
"url": "https://aka.ms/cli-m365"
}
]
}
Expand Down