diff --git a/.claude/skills/docs-review/SKILL.md b/.claude/skills/docs-review/SKILL.md index 61a37a5c..12060b5a 100644 --- a/.claude/skills/docs-review/SKILL.md +++ b/.claude/skills/docs-review/SKILL.md @@ -51,6 +51,7 @@ Perform critical editorial reviews as a tech writer / copyeditor, focusing on cl - **Heading count**: 20+ headings signals over-segmentation; consolidate - **Information flow**: Conceptual content (trade-offs, when to use) should come before implementation details, not after - **Admonition weight**: Tips, notes, and warnings should be supplementary. If an admonition contains primary feature documentation (full YAML examples, the only explanation of a field or concept), it should be promoted to a proper section or its own page. A good test: if this admonition is the only place a feature is documented, it's not a tip - it's a section that needs a heading and ToC visibility. +- **Enterprise construct usage**: Three constructs exist for enterprise content - check they're used correctly. `:::enterprise` admonitions are for callout blocks (2-4 sentences, max 1-2 per page). `` is for inline feature labels next to headings or list items. `className: 'enterprise-only'` in the sidebar is for enterprise-only pages. Flag misuse: enterprise admonitions used for single-feature labels (use `` instead), badge used on entire page descriptions (use the admonition instead), or excessive enterprise admonitions on a single page. - **Diataxis alignment**: Don't mix tutorials, how-tos, reference, and concepts in one doc without clear separation ### LLM-Generated Content Patterns diff --git a/AGENTS.md b/AGENTS.md index 3c3bca8a..adc647e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -263,7 +263,43 @@ This website is built using Docusaurus, which has some specific requirements and - Titles are added using the `title="..."` attribute in the opening code fence. - Line highlights are added using comma-separated `{number}` or `{start-end}` ranges in the opening code fence, or `highlight-next-line`, `highlight-start`, and `highlight-end` comments within the code block. - Use admonitions for notes, tips, warnings, and other annotations. This provides a consistent look and feel across the site. - - Use the `:::type` syntax to define the admonition type: `note`, `tip`, `info`, `warning`, or `danger`. Use square brackets to add a custom title, e.g. `:::info[Title]`. Add empty lines around the start and end directives. + - Use the `:::type` syntax to define the admonition type: `note`, `tip`, `info`, `warning`, `danger`, or `enterprise`. Use square brackets to add a custom title, e.g. `:::info[Title]`. Add empty lines around the start and end directives. + - The `:::enterprise` admonition is for Stacklok Enterprise content only - see "Enterprise content constructs" below. - Place images in `static/img` using WebP, PNG, or SVG format. - Use the `ThemedImage` component to provide both light and dark mode screenshots for apps/UIs that support both. Typically used with the `useBaseUrl` hook to construct the image paths. Both require import statements. - Use the `Tabs` and `TabItem` components to create tabbed content sections. These are in the global scope and do not require imports. +- Use the `EnterpriseBadge` component to label individual features or capabilities as enterprise-only. This is in the global scope and does not require imports. See "Enterprise content constructs" below. + +### Enterprise content constructs + +Three constructs are available for presenting Stacklok Enterprise content inline with OSS documentation. Use the right one for the context: + +**`:::enterprise` admonition** - for callout content within OSS pages, typically 2-4 sentences describing an Enterprise capability with a link to the Enterprise landing page. Use when Enterprise adds a meaningful capability to the topic being documented. Don't overuse - one per page is typical, two is the practical maximum. + +```mdx +:::enterprise + +Stacklok Enterprise includes turnkey integrations for common identity providers. Instead of manually configuring OIDC, use the built-in Okta or Entra ID integration to map IdP groups directly to ToolHive roles and policy sets. + +[Learn more about Stacklok Enterprise](/toolhive/enterprise). + +::: +``` + +**``** - inline label for tagging individual features, capabilities, or configuration options as enterprise-only. Works next to headings, in lists, or inline with text. Use when a specific feature within a broader page is enterprise-only. + +```mdx +### Session pinning + +- **Automatic failover** - connections are automatically rerouted when a node becomes unavailable. +``` + +**`className: 'enterprise-only'` sidebar badge** - for marking enterprise-only pages in the sidebar navigation. Applied in `sidebars.ts`, not in the page itself. Renders a small "ENT" pill badge with a tooltip on hover. + +```ts title="sidebars.ts" +{ + type: 'doc', + id: 'toolhive/guides-enterprise/config-server', + className: 'enterprise-only', +} +``` diff --git a/docs/theme-preview.mdx b/docs/theme-preview.mdx index bac15d71..bc7a03dd 100644 --- a/docs/theme-preview.mdx +++ b/docs/theme-preview.mdx @@ -174,6 +174,48 @@ This is getting silly ::::: +## Enterprise constructs + +Components for presenting Stacklok Enterprise content inline with OSS docs. + +### Enterprise admonition + +Custom `:::enterprise` admonition for callout content within OSS pages. Uses the +Stacklok symbol as the icon and a teal color palette distinct from the other +admonition types. Supports custom titles via `:::enterprise[My title]`. + +:::enterprise + +Stacklok Enterprise includes turnkey integrations for common identity providers. +Instead of manually configuring OIDC, use the built-in Okta or Entra ID +integration to map IdP groups directly to ToolHive roles and policy sets. + +[Learn more about Stacklok Enterprise](/toolhive/enterprise). + +::: + +### Enterprise badge + +Inline `` component for labeling individual features or +capabilities within a page. Works next to headings, in lists, or inline with +text. + +#### Session pinning + +Sessions can be pinned to a specific node for the duration of a connection. + +- **Automatic failover** - connections are automatically + rerouted when a node becomes unavailable. +- **Manual failover** - connections can be manually rerouted by an + administrator. + +### Sidebar badge + +Enterprise-only pages can be marked with an `ENT` badge in the sidebar by adding +`className: 'enterprise-only'` to the sidebar item in `sidebars.ts`. The badge +includes a tooltip on hover. See "Stacklok Enterprise" in the sidebar for an +example. + ## Tables A standard Markdown table: diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 6ac68b95..0e47e307 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -171,6 +171,10 @@ const config: Config = { // Populate lastUpdatedAt metadata for JSON-LD structured data. // The rendered timestamp is hidden via CSS (.theme-last-updated). showLastUpdateTime: true, + admonitions: { + keywords: ['enterprise'], + extendDefaults: true, + }, }, blog: { blogTitle: 'ToolHive Updates and Announcements', diff --git a/eslint.config.mjs b/eslint.config.mjs index a7e1a248..8697d0a0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -45,6 +45,7 @@ export default [ Tabs: 'readonly', TabItem: 'readonly', MCPMetadata: 'readonly', + EnterpriseBadge: 'readonly', }, }, }, diff --git a/src/components/EnterpriseBadge/index.tsx b/src/components/EnterpriseBadge/index.tsx new file mode 100644 index 00000000..013c217b --- /dev/null +++ b/src/components/EnterpriseBadge/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import styles from './styles.module.css'; + +export default function EnterpriseBadge(): React.ReactNode { + return ( + + Enterprise + + ); +} diff --git a/src/components/EnterpriseBadge/styles.module.css b/src/components/EnterpriseBadge/styles.module.css new file mode 100644 index 00000000..ef302f83 --- /dev/null +++ b/src/components/EnterpriseBadge/styles.module.css @@ -0,0 +1,20 @@ +.badge { + display: inline-flex; + align-items: center; + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.02em; + line-height: 1; + padding: 0.2em 0.6em; + border: 1.5px solid #2a9d8f; + border-radius: 6px; + color: #1a5c54; + vertical-align: middle; + margin-left: 0.4em; + white-space: nowrap; +} + +:global([data-theme='dark']) .badge { + color: #c8ece7; + border-color: #2a9d8f; +} diff --git a/src/css/custom.css b/src/css/custom.css index 328673b2..151fab84 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -382,6 +382,71 @@ details summary:hover { --ifm-alert-foreground-color: var(--stacklok-green-light); } +/* Enterprise admonitions - teal */ +.alert--enterprise { + --ifm-alert-background-color: #d5f0ec; + --ifm-alert-border-color: #1a8a7d; + --ifm-alert-foreground-color: #0f4f48; +} + +[data-theme='dark'] .alert--enterprise { + --ifm-alert-background-color: #1a3330; + --ifm-alert-border-color: #2a9d8f; + --ifm-alert-foreground-color: #c8ece7; +} + +/* Sidebar badge for enterprise-only pages */ +.enterprise-only > .menu__link { + position: relative; +} + +.enterprise-only > .menu__link::after { + content: 'ENT'; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.55rem; + font-weight: 700; + letter-spacing: 0.03em; + line-height: 1; + padding: 0.15em 0.4em; + margin-left: 0.75em; + border: 1.5px solid #2a9d8f; + border-radius: 999px; + color: #1a5c54; + flex-shrink: 0; +} + +.enterprise-only > .menu__link:hover::before, +.enterprise-only > .menu__link:focus-visible::before { + content: 'Stacklok Enterprise feature'; + position: absolute; + left: 50%; + bottom: calc(100% + 6px); + transform: translateX(-50%); + padding: 0.3em 0.6em; + font-size: 0.7rem; + font-weight: 500; + line-height: 1.3; + white-space: nowrap; + color: #fff; + background: #1a5c54; + border-radius: 4px; + pointer-events: none; + z-index: 10; +} + +[data-theme='dark'] .enterprise-only > .menu__link::after { + color: #c8ece7; + border-color: #2a9d8f; +} + +[data-theme='dark'] .enterprise-only > .menu__link:hover::before, +[data-theme='dark'] .enterprise-only > .menu__link:focus-visible::before { + background: #1a5c54; + color: #c8ece7; +} + /* Screenshot styling with subtle border */ .screenshot { border: 1px solid var(--ifm-table-border-color); diff --git a/src/theme/Admonition/Types.tsx b/src/theme/Admonition/Types.tsx new file mode 100644 index 00000000..3b701ba9 --- /dev/null +++ b/src/theme/Admonition/Types.tsx @@ -0,0 +1,46 @@ +import React, { type ReactNode } from 'react'; +import DefaultAdmonitionTypes from '@theme-original/Admonition/Types'; +import AdmonitionLayout from '@theme/Admonition/Layout'; + +const StacklokIcon = () => ( + +); + +interface EnterpriseAdmonitionProps { + children: ReactNode; + title?: string; + className?: string; +} + +function EnterpriseAdmonition(props: EnterpriseAdmonitionProps): ReactNode { + return ( + } + title={props.title ?? 'Stacklok Enterprise'} + className={`alert alert--enterprise ${props.className ?? ''}`} + > + {props.children} + + ); +} + +const AdmonitionTypes = { + ...DefaultAdmonitionTypes, + enterprise: EnterpriseAdmonition, +}; + +export default AdmonitionTypes; diff --git a/src/theme/MDXComponents.tsx b/src/theme/MDXComponents.tsx index aef9f3e0..1db71f66 100644 --- a/src/theme/MDXComponents.tsx +++ b/src/theme/MDXComponents.tsx @@ -10,6 +10,7 @@ import MDXComponents from '@theme-original/MDXComponents'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import MCPMetadata from '@site/src/components/MCPMetadata'; +import EnterpriseBadge from '@site/src/components/EnterpriseBadge'; export default { // Reusing the default mapping @@ -18,4 +19,5 @@ export default { Tabs, TabItem, MCPMetadata, + EnterpriseBadge, };