feat(docs): simplify quickstart code-block styling

Remove the fake terminal chrome (traffic lights + zsh header) and language
pill from bash blocks, and the teal left-accent from output blocks. Bash
fences now render as minimal cards; text fences route to a muted "output"
preview. Make detectLanguage recursive and enable addLanguageClass in
source.config.ts so Shiki tokens carry through to the renderer. Switch
Shiki themes to min-light / github-dark and disable monospace ligatures so
flag pairs like --agents keep a visible space.
This commit is contained in:
Andrey Avtomonov 2026-05-18 19:14:43 +02:00
parent e875cbab54
commit a39ce02e55
4 changed files with 44 additions and 109 deletions

View file

@ -231,57 +231,10 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
margin: 0;
}
/* ── Mode A: Terminal ─────────────────────── */
.ktx-code-terminal {
background: #0c1417;
border: 1px solid rgba(255, 255, 255, 0.08);
color: #c8c3bc;
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.1),
0 12px 32px -16px rgba(0, 0, 0, 0.3);
}
.ktx-code-terminal:hover {
border-color: rgba(34, 211, 238, 0.2);
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.1),
0 14px 32px -12px rgba(34, 211, 238, 0.18);
}
.ktx-code-terminal-head {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent);
}
.ktx-tl-dot {
width: 11px;
height: 11px;
border-radius: 999px;
flex-shrink: 0;
}
.ktx-code-terminal-label {
margin-left: 8px;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.02em;
color: rgba(255, 255, 255, 0.4);
}
.ktx-code-body-terminal {
background: transparent !important;
color: #c8c3bc !important;
}
/* ── Mode D: Output preview (wizard prompts, status output) ── */
.ktx-code-output {
background: var(--color-fd-muted);
border: 1px solid var(--color-fd-border);
border-left: 3px solid color-mix(in oklch, var(--color-fd-primary) 50%, var(--color-fd-border));
position: relative;
box-shadow: 0 1px 2px rgba(27, 27, 24, 0.02);
}
@ -289,17 +242,14 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
.dark .ktx-code-output {
background: #111a1e;
border-color: rgba(255, 255, 255, 0.05);
border-left-color: rgba(34, 211, 238, 0.25);
}
.ktx-code-output:hover {
border-color: color-mix(in oklch, var(--color-fd-primary) 25%, var(--color-fd-border));
border-left-color: var(--color-fd-primary);
}
.dark .ktx-code-output:hover {
border-color: rgba(255, 255, 255, 0.08);
border-left-color: rgba(34, 211, 238, 0.45);
}
.ktx-code-output-label {
@ -460,26 +410,6 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
border-color: rgba(34, 211, 238, 0.2);
}
.ktx-code-minimal-lang {
position: absolute;
top: 8px;
left: 14px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-fd-muted-foreground);
font-family: var(--font-display), var(--font-sans), sans-serif;
opacity: 0;
transition: opacity 0.2s var(--ktx-ease);
pointer-events: none;
z-index: 1;
}
.ktx-code-minimal:hover .ktx-code-minimal-lang {
opacity: 0.5;
}
.ktx-code-minimal-copy {
position: absolute !important;
top: 7px !important;
@ -1073,8 +1003,7 @@ body::after {
.pill-badge .pill-dot { animation: none; }
.card-lift { transition: none; }
.ktx-code,
.ktx-code-minimal-copy,
.ktx-code-minimal-lang {
.ktx-code-minimal-copy {
transition: none;
}
#nd-sidebar div[data-state]:not([class]) > button[data-state] svg {

View file

@ -13,7 +13,7 @@ type Props = ComponentPropsWithoutRef<"pre"> & {
"data-language"?: string;
};
const TERMINAL_LANGS = new Set(["bash", "sh", "shell", "zsh"]);
const OUTPUT_LANGS = new Set(["text", "plain", "plaintext", "console", "output"]);
const WIZARD_GLYPHS = /^\s*[◆◇◯◐○●]/;
function extractText(node: ReactNode): string {
@ -27,6 +27,33 @@ function extractText(node: ReactNode): string {
return "";
}
function findLanguageInNode(node: ReactNode): string | null {
if (!isValidElement(node)) return null;
const props = (node as ReactElement<{
className?: string;
"data-language"?: string;
children?: ReactNode;
}>).props;
const dataLang = props["data-language"];
if (typeof dataLang === "string" && dataLang) return dataLang;
const className = typeof props.className === "string" ? props.className : "";
const m = className.match(/language-([\w-]+)/);
if (m) return m[1];
const children = props.children;
if (Array.isArray(children)) {
for (const child of children) {
const found = findLanguageInNode(child);
if (found) return found;
}
} else if (children) {
return findLanguageInNode(children);
}
return null;
}
function detectLanguage(props: Props, children: ReactNode): string | null {
const dataLang = props["data-language"];
if (typeof dataLang === "string" && dataLang) return dataLang;
@ -35,14 +62,7 @@ function detectLanguage(props: Props, children: ReactNode): string | null {
const m = className.match(/language-([\w-]+)/);
if (m) return m[1];
if (isValidElement(children)) {
const childProps = (children as ReactElement<{ className?: string }>).props;
const childClass = typeof childProps.className === "string" ? childProps.className : "";
const cm = childClass.match(/language-([\w-]+)/);
if (cm) return cm[1];
}
return null;
return findLanguageInNode(children);
}
export function CodeBlock(props: Props) {
@ -50,32 +70,11 @@ export function CodeBlock(props: Props) {
const language = detectLanguage(props, children);
const codeText = extractText(children);
const isTerminal = language !== null && TERMINAL_LANGS.has(language);
const isOutput = !isTerminal && WIZARD_GLYPHS.test(codeText);
const hasTitle = typeof title === "string" && title.length > 0;
// Mode A - Terminal (commands the user types)
if (isTerminal) {
return (
<div className="not-prose ktx-code ktx-code-terminal group">
<div className="ktx-code-terminal-head">
<span className="ktx-tl-dot" style={{ background: "#ff5f57" }} />
<span className="ktx-tl-dot" style={{ background: "#febc2e" }} />
<span className="ktx-tl-dot" style={{ background: "#28c840" }} />
<span className="ktx-code-terminal-label">
{hasTitle ? title : "zsh"}
</span>
<CopyButton
text={codeText}
className="ml-auto text-white/80"
/>
</div>
<pre {...rest} className="ktx-code-body ktx-code-body-terminal">
{children}
</pre>
</div>
);
}
const isOutput =
!hasTitle &&
(WIZARD_GLYPHS.test(codeText) ||
(language !== null && OUTPUT_LANGS.has(language)));
// Mode D - Output preview (wizard prompts, terminal output)
if (isOutput) {
@ -110,7 +109,6 @@ export function CodeBlock(props: Props) {
// Mode C - Minimal default
return (
<div className="not-prose ktx-code ktx-code-minimal group relative">
{language && <span className="ktx-code-minimal-lang">{language}</span>}
<CopyButton text={codeText} className="ktx-code-minimal-copy" />
<pre {...rest} className="ktx-code-body ktx-code-body-minimal">
{children}

View file

@ -25,7 +25,7 @@ export function CopyButton({ text, className = "" }: Props) {
type="button"
onClick={onClick}
aria-label={copied ? "Copied" : "Copy code"}
className={`inline-flex items-center justify-center w-9 h-9 rounded-md transition-all hover:bg-white/5 ${className}`}
className={`inline-flex items-center justify-center w-9 h-9 rounded-md transition-all hover:bg-fd-muted ${className}`}
>
{copied ? (
<svg

View file

@ -5,5 +5,13 @@ export const docs = defineDocs({
});
export default defineConfig({
mdxOptions: {},
mdxOptions: {
rehypeCodeOptions: {
addLanguageClass: true,
themes: {
light: "min-light",
dark: "github-dark",
},
},
},
});