Skip to content
Merged
68 changes: 0 additions & 68 deletions examples/ShellDocs.Preview/Components/Demos/Callout.razor

This file was deleted.

4 changes: 2 additions & 2 deletions examples/ShellDocs.Preview/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
o.AddNavLink("Showcase", "/showcase");
o.AddNavLink("Blog", "/blog");

// Demo components available for <razor:preview> blocks in markdown.
o.RegisterComponent<ShellDocs.Preview.Components.Demos.Callout>();
// Callout, Card, Steps, FileTree, CodeGroup, LinkCard etc. are
// auto-registered by AddShellDocs — no extra work needed here.
});

var app = builder.Build();
Expand Down
45 changes: 45 additions & 0 deletions examples/ShellDocs.Preview/content/docs/components/card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: Card
description: Bordered cards with title, description, optional icon, and optional link.
category: Components
order: 12
---

# Card

`<Card>` renders a bordered rounded panel with a title, description, and optional icon. Pass `Href` to make the whole card a link.

## Basic

```razor:preview
<Card Title="Read the docs" Description="Get started with a walkthrough of what's inside." />
```

## As a link

Add `Href` and the card becomes a link with a hover accent.

```razor:preview
<Card Title="Configuration" Description="Every option on ShellDocsOptions, one table." Href="/docs/introduction" />
```

## In a grid

Wrap Cards in `<CardGrid Columns="2">` for a responsive 2-col (or 3-col) layout.

```razor:preview
<CardGrid Columns="2">
<Card Title="Fast" Description="Instant page loads, client-rendered islands only where needed." />
<Card Title="Themeable" Description="One CSS var for every colour; override in your own stylesheet." />
<Card Title="Composable" Description="Every layout, header, and TOC is a Blazor component you can swap." />
<Card Title="Static-ready" Description="Deploy to GH Pages, Cloudflare, or S3 as pre-rendered HTML." />
</CardGrid>
```

## LinkCard

For "further reading" panels, `<LinkCard>` is a compact one-line variant with a hover arrow.

```razor:preview
<LinkCard Title="Frontmatter reference" Description="Every property you can set at the top of a .md file." Href="/docs/markdown-syntax" />
```
38 changes: 38 additions & 0 deletions examples/ShellDocs.Preview/content/docs/components/code-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: CodeGroup
description: Tabbed code samples that sync across the page.
category: Components
order: 25
---

# CodeGroup

`<CodeGroup>` groups multiple code samples into a tabbed panel — the reader picks one, sees that source, ignores the others. The most common use is a per-package-manager install snippet.

## Basic

```razor:preview
<CodeGroup>
<CodeTab Label="npm">npm install shelldocs</CodeTab>
<CodeTab Label="pnpm">pnpm add shelldocs</CodeTab>
<CodeTab Label="yarn">yarn add shelldocs</CodeTab>
</CodeGroup>
```

## Sync groups

Pass `SyncKey` and every `<CodeGroup>` on the page with the same key switches together. Pick "pnpm" here — every other snippet with `SyncKey="pkg"` on the page will also read pnpm.

```razor:preview
<CodeGroup SyncKey="pkg">
<CodeTab Label="npm">npm run build</CodeTab>
<CodeTab Label="pnpm">pnpm build</CodeTab>
<CodeTab Label="yarn">yarn build</CodeTab>
</CodeGroup>
```

## Notes

- The first `<CodeTab>` in source order is the default selection on first render (unless overridden by an active sync group).
- Choose stable sync keys — the sync state uses the key as its dictionary bucket, so renaming a key resets everyone's pick.
- Sync is per-circuit today. Cross-session persistence via `localStorage` lands in a follow-up.
42 changes: 42 additions & 0 deletions examples/ShellDocs.Preview/content/docs/components/filetree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: FileTree
description: Static folder / file visualisation for project layout diagrams.
category: Components
order: 50
---

# FileTree

`<FileTree>` renders a static folder-and-file tree — the kind of "here's what your project structure looks like" diagram you see in every framework's getting-started guide.

## Basic

```razor:preview
<FileTree>
<FileTreeItem Name="my-docs" IsFolder="true">
<FileTreeItem Name="content" IsFolder="true">
<FileTreeItem Name="docs" IsFolder="true">
<FileTreeItem Name="introduction.md" />
<FileTreeItem Name="installation.md" />
<FileTreeItem Name="meta.json" Comment="sidebar order" />
</FileTreeItem>
</FileTreeItem>
<FileTreeItem Name="Components" IsFolder="true">
<FileTreeItem Name="Pages" IsFolder="true">
<FileTreeItem Name="DocsPage.razor" Highlight="true" Comment="routes /docs/*" />
<FileTreeItem Name="Home.razor" />
</FileTreeItem>
<FileTreeItem Name="App.razor" />
</FileTreeItem>
<FileTreeItem Name="Program.cs" />
<FileTreeItem Name="my-docs.csproj" />
</FileTreeItem>
</FileTree>
```

## Props

- `Name` — the file or folder name shown next to the glyph
- `IsFolder` — draws the folder glyph and enables nested children
- `Comment` — muted italic comment shown to the right (e.g. `// sidebar order`)
- `Highlight` — soft warning-coloured background on the label to draw attention to a specific line
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"title": "Components",
"pages": ["callout", "code-block", "tabs"]
"pages": ["callout", "card", "code-block", "code-group", "steps", "tabs", "filetree"]
}
35 changes: 35 additions & 0 deletions examples/ShellDocs.Preview/content/docs/components/steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: Steps
description: Vertically-numbered onboarding sequence with a connecting rail.
category: Components
order: 40
---

# Steps

Use `<Steps>` to lay out an onboarding sequence — install, configure, run. Each `<Step>` is auto-numbered by its position; you don't manage the counter.

## Basic

```razor:preview
<Steps>
<Step Title="Install the CLI">
Install the ShellDocs global tool from NuGet.
</Step>
<Step Title="Scaffold a project">
Run <code>shelldocs init</code> in your repo root.
</Step>
<Step Title="Author your first page">
Edit <code>content/docs/introduction.md</code>. It hot-reloads.
</Step>
<Step Title="Ship it">
<code>shelldocs build</code> emits a static site. Deploy anywhere.
</Step>
</Steps>
```

## Notes

- Each Step's `Title` is optional — omit for a rendered numbered paragraph.
- Nest richer content (paragraphs, code, callouts) via `ChildContent`.
- The rail is drawn from the outer `<Steps>` `border-left`; step number chips overlay it.
40 changes: 40 additions & 0 deletions src/ShellDocs.Components/Content/Callout.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@namespace ShellDocs.Components.Content

<div class="callout callout-@Variant.ToLowerInvariant()">
<span class="callout-icon" aria-hidden="true">
@switch (Variant.ToLowerInvariant())
{
case "warning":
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
break;
case "danger":
case "error":
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
break;
case "tip":
case "success":
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6M10 22h4M12 2a7 7 0 0 0-4 12.7c.5.5 1 1.3 1 2.3v1h6v-1c0-1 .5-1.8 1-2.3A7 7 0 0 0 12 2z"/></svg>
break;
default: /* info */
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
break;
}
</span>
<div class="callout-body">
@if (!string.IsNullOrEmpty(Title))
{
<div class="callout-title">@Title</div>
}
<div class="callout-content">
@if (ChildContent is not null) { @ChildContent }
else if (!string.IsNullOrEmpty(Text)) { @Text }
</div>
</div>
</div>

@code {
[Parameter] public string Variant { get; set; } = "info";
[Parameter] public string? Title { get; set; }
[Parameter] public string? Text { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
}
27 changes: 27 additions & 0 deletions src/ShellDocs.Components/Content/Callout.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.callout {
display: flex;
gap: 0.85rem;
padding: 0.9rem 1.1rem;
border: 1px solid var(--border);
border-radius: calc(var(--radius) + 2px);
background: color-mix(in oklch, var(--card) 70%, var(--background));
line-height: 1.6;
font-size: 0.9rem;
margin: 1.25rem 0;
}
.callout-icon { flex-shrink: 0; display: inline-flex; align-items: flex-start; padding-top: 0.05rem; }
.callout-icon svg { width: 1.05rem; height: 1.05rem; }
.callout-body { flex: 1; min-width: 0; }
.callout-title { font-weight: 600; margin-bottom: 0.2rem; letter-spacing: -0.005em; color: var(--foreground); }
.callout-content { color: var(--muted-foreground); }
.callout-content > *:first-child { margin-top: 0; }
.callout-content > *:last-child { margin-bottom: 0; }

.callout-info { border-color: color-mix(in oklch, var(--info) 30%, var(--border)); }
.callout-info .callout-icon { color: var(--info); }
.callout-warning { border-color: color-mix(in oklch, var(--warning) 35%, var(--border)); }
.callout-warning .callout-icon { color: var(--warning); }
.callout-danger, .callout-error { border-color: color-mix(in oklch, var(--error) 35%, var(--border)); }
.callout-danger .callout-icon, .callout-error .callout-icon { color: var(--error); }
.callout-tip, .callout-success { border-color: color-mix(in oklch, var(--success) 30%, var(--border)); }
.callout-tip .callout-icon, .callout-success .callout-icon { color: var(--success); }
63 changes: 63 additions & 0 deletions src/ShellDocs.Components/Content/Card.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@namespace ShellDocs.Components.Content

@{
var isLink = !string.IsNullOrEmpty(Href);
var tag = isLink ? "a" : "div";
}

@if (isLink)
{
<a class="doc-card" href="@Href" target="@(External ? "_blank" : null)" rel="@(External ? "noopener" : null)">
@Body
</a>
}
else
{
<div class="doc-card">
@Body
</div>
}

@code {
[Parameter] public string? Title { get; set; }
[Parameter] public string? Description { get; set; }
[Parameter] public string? Href { get; set; }
[Parameter] public string? IconSvg { get; set; }
[Parameter] public bool External { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }

private RenderFragment Body => __b =>
{
if (!string.IsNullOrEmpty(IconSvg))
{
__b.OpenElement(0, "span");
__b.AddAttribute(1, "class", "doc-card-icon");
__b.AddMarkupContent(2, IconSvg);
__b.CloseElement();
}
__b.OpenElement(3, "div");
__b.AddAttribute(4, "class", "doc-card-body");
if (!string.IsNullOrEmpty(Title))
{
__b.OpenElement(5, "div");
__b.AddAttribute(6, "class", "doc-card-title");
__b.AddContent(7, Title);
__b.CloseElement();
}
if (!string.IsNullOrEmpty(Description))
{
__b.OpenElement(8, "div");
__b.AddAttribute(9, "class", "doc-card-description");
__b.AddContent(10, Description);
__b.CloseElement();
}
if (ChildContent is not null)
{
__b.OpenElement(11, "div");
__b.AddAttribute(12, "class", "doc-card-content");
__b.AddContent(13, ChildContent);
__b.CloseElement();
}
__b.CloseElement();
};
}
Loading
Loading