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
30 changes: 30 additions & 0 deletions src/ShellDocs.Components/Chrome/BrandLogo.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@namespace ShellDocs.Components.Chrome
@inject ShellDocsOptions Options

@if (!string.IsNullOrWhiteSpace(Options.LogoSvg))
{
<span class="brand-logo-svg" style="height: @Height" aria-label="@Alt" role="img">
@((MarkupString)Options.LogoSvg)
</span>
}
else if (HasUrlLogo)
{
var light = Options.LogoLight ?? Options.LogoDark!;
var dark = Options.LogoDark ?? Options.LogoLight!;

<img class="brand-logo brand-logo-light" src="@light" alt="@Alt" style="height: @Height" />
@if (!string.Equals(light, dark, StringComparison.Ordinal))
{
<img class="brand-logo brand-logo-dark" src="@dark" alt="@Alt" style="height: @Height" />
}
}
else
{
<span class="brand-logo-dot"></span>
}

@code {
private bool HasUrlLogo => !string.IsNullOrEmpty(Options.LogoLight) || !string.IsNullOrEmpty(Options.LogoDark);
private string Alt => Options.LogoAlt ?? Options.SiteName ?? "";
private string Height => Options.LogoHeight.ToString(System.Globalization.CultureInfo.InvariantCulture) + "rem";
}
30 changes: 30 additions & 0 deletions src/ShellDocs.Components/Chrome/BrandLogo.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.brand-logo {
display: inline-block;
width: auto;
flex-shrink: 0;
object-fit: contain;
}

.brand-logo-dark { display: none; }
:root.dark .brand-logo-light { display: none; }
:root.dark .brand-logo-dark { display: inline-block; }

.brand-logo-svg {
display: inline-flex;
align-items: center;
flex-shrink: 0;
color: var(--foreground);
}
::deep .brand-logo-svg > svg {
height: 100%;
width: auto;
display: block;
}

.brand-logo-dot {
width: 1.125rem;
height: 1.125rem;
border-radius: 4px;
background: var(--foreground);
flex-shrink: 0;
}
2 changes: 1 addition & 1 deletion src/ShellDocs.Components/Chrome/DocsFooter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div class="docs-footer-inner">
<div class="docs-footer-brand">
<div class="docs-footer-mark">
<span class="docs-footer-logo-dot"></span>
<BrandLogo />
<span class="docs-footer-brand-name">@(!string.IsNullOrEmpty(Options.SiteName) ? Options.SiteName : "ShellDocs")</span>
</div>
@if (!string.IsNullOrEmpty(Options.SiteTagline))
Expand Down
6 changes: 0 additions & 6 deletions src/ShellDocs.Components/Chrome/DocsFooter.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@

::deep .docs-footer-brand { display: flex; flex-direction: column; gap: 0.85rem; }
::deep .docs-footer-mark { display: inline-flex; align-items: center; gap: 0.5rem; }
::deep .docs-footer-logo-dot {
width: 1.125rem;
height: 1.125rem;
border-radius: 4px;
background: var(--foreground);
}
::deep .docs-footer-brand-name { font-weight: 600; font-size: 1rem; letter-spacing: -0.01em; }
::deep .docs-footer-tagline {
margin: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/ShellDocs.Components/Chrome/DocsHeader.razor
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</svg>
</button>
<a class="docs-header-brand" href="/">
<span class="docs-header-logo-dot"></span>
<BrandLogo />
<span class="docs-header-brand-name">@(!string.IsNullOrEmpty(Options.SiteName) ? Options.SiteName : "Docs")</span>
</a>

Expand Down
8 changes: 0 additions & 8 deletions src/ShellDocs.Components/Chrome/DocsHeader.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,6 @@
}
.docs-header-brand:hover { opacity: 0.85; }

.docs-header-logo-dot {
width: 1.125rem;
height: 1.125rem;
border-radius: 4px;
background: var(--foreground);
flex-shrink: 0;
}

.docs-header-brand-name { font-weight: 600; letter-spacing: -0.01em; }

.docs-header-nav {
Expand Down
2 changes: 1 addition & 1 deletion src/ShellDocs.Components/Chrome/DocsSidebarHeader.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div class="docs-sidebar-header">
<div class="docs-sidebar-brand-row">
<a class="docs-sidebar-brand" href="/">
<span class="docs-sidebar-logo-dot"></span>
<BrandLogo />
<span class="docs-sidebar-brand-name">@(!string.IsNullOrEmpty(Options.SiteName) ? Options.SiteName : "Docs")</span>
</a>
<button type="button" class="docs-sidebar-collapse" @onclick="Collapse.Toggle" aria-label="Collapse sidebar" title="Collapse sidebar">
Expand Down
8 changes: 0 additions & 8 deletions src/ShellDocs.Components/Chrome/DocsSidebarHeader.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@
}
.docs-sidebar-collapse:hover { color: var(--foreground); background: var(--muted); }
.docs-sidebar-collapse svg { width: 1rem; height: 1rem; }
.docs-sidebar-logo-dot {
width: 1.125rem;
height: 1.125rem;
border-radius: 4px;
background: var(--foreground);
flex-shrink: 0;
}

.docs-sidebar-search {
display: inline-flex;
align-items: center;
Expand Down
23 changes: 23 additions & 0 deletions src/ShellDocs.Components/ShellDocsOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ public class ShellDocsOptions
public string SiteName { get; set; } = "";
public string? SiteTagline { get; set; }
public string? GitHubRepo { get; set; }

public string? LogoLight { get; set; }
public string? LogoDark { get; set; }
public string? LogoAlt { get; set; }
public double LogoHeight { get; set; } = 1.375;
// Rendered as MarkupString — must be trusted content the consumer authored, not user input.
public string? LogoSvg { get; set; }
public ShellDocsTheme Theme { get; set; } = ShellDocsTheme.Shadcn;
public DocsLayoutVariant LayoutVariant { get; set; } = DocsLayoutVariant.TopNav;

Expand Down Expand Up @@ -117,6 +124,22 @@ public ShellDocsOptions AddPackage(string id, string title, string description,
return this;
}

public ShellDocsOptions SetLogo(string url, string? alt = null)
{
LogoLight = url;
LogoDark = url;
if (alt is not null) LogoAlt = alt;
return this;
}

public ShellDocsOptions SetLogo(string lightUrl, string darkUrl, string? alt = null)
{
LogoLight = lightUrl;
LogoDark = darkUrl;
if (alt is not null) LogoAlt = alt;
return this;
}

internal TypeRegistry BuildTypeRegistry()
{
var registry = new TypeRegistry();
Expand Down
50 changes: 50 additions & 0 deletions tests/ShellDocs.Tests/BrandLogoOptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using ShellDocs.Components;
using Xunit;

namespace ShellDocs.Tests;

public class BrandLogoOptionsTests
{
[Fact]
public void SetLogo_SingleUrl_AppliesToBothThemes()
{
var options = new ShellDocsOptions().SetLogo("/img/logo.svg");

Assert.Equal("/img/logo.svg", options.LogoLight);
Assert.Equal("/img/logo.svg", options.LogoDark);
}

[Fact]
public void SetLogo_LightAndDark_KeepsBothDistinct()
{
var options = new ShellDocsOptions().SetLogo("/img/light.svg", "/img/dark.svg", "Brand");

Assert.Equal("/img/light.svg", options.LogoLight);
Assert.Equal("/img/dark.svg", options.LogoDark);
Assert.Equal("Brand", options.LogoAlt);
}

[Fact]
public void LogoHeight_DefaultsToOnePointThreeSevenFiveRem()
{
Assert.Equal(1.375, new ShellDocsOptions().LogoHeight);
}

[Fact]
public void Logos_DefaultToNull_SoDotFallbackRenders()
{
var options = new ShellDocsOptions();
Assert.Null(options.LogoLight);
Assert.Null(options.LogoDark);
Assert.Null(options.LogoSvg);
}

[Fact]
public void LogoSvg_HoldsRawMarkupUntouched()
{
const string svg = "<svg viewBox=\"0 0 24 24\"><path d=\"M0 0h24v24H0z\" fill=\"currentColor\"/></svg>";
var options = new ShellDocsOptions { LogoSvg = svg };

Assert.Equal(svg, options.LogoSvg);
}
}
Loading