diff --git a/.changeset/plain-code-highlighted.md b/.changeset/plain-code-highlighted.md
new file mode 100644
index 000000000..4ba0e6dc4
--- /dev/null
+++ b/.changeset/plain-code-highlighted.md
@@ -0,0 +1,5 @@
+---
+"@cloudflare/kumo": patch
+---
+
+Add a plain CodeHighlighted variant for frameless, padding-free code blocks with a correctly positioned copy button.
diff --git a/packages/kumo-docs-astro/src/components/demos/CodeHighlightedDemo.tsx b/packages/kumo-docs-astro/src/components/demos/CodeHighlightedDemo.tsx
index 867b2cd68..b4f882b35 100644
--- a/packages/kumo-docs-astro/src/components/demos/CodeHighlightedDemo.tsx
+++ b/packages/kumo-docs-astro/src/components/demos/CodeHighlightedDemo.tsx
@@ -186,6 +186,53 @@ export function CodeHighlightedCopyButtonDemo() {
);
}
+/** Frameless code block for embedding in another surface */
+export function CodeHighlightedPlainDemo() {
+ return (
+
+
+
+ );
+}
+
/** Full featured example */
export function CodeHighlightedFullFeaturedDemo() {
return (
diff --git a/packages/kumo-docs-astro/src/components/skill/design-tips.tsx b/packages/kumo-docs-astro/src/components/skill/design-tips.tsx
index 11204992a..19a46b2f8 100644
--- a/packages/kumo-docs-astro/src/components/skill/design-tips.tsx
+++ b/packages/kumo-docs-astro/src/components/skill/design-tips.tsx
@@ -24,9 +24,10 @@ interface CodeExampleProps {
export function CodeExample({ code }: CodeExampleProps) {
return (
);
}
diff --git a/packages/kumo-docs-astro/src/pages/components/code-highlighted.mdx b/packages/kumo-docs-astro/src/pages/components/code-highlighted.mdx
index 2910ecdb8..3df018353 100644
--- a/packages/kumo-docs-astro/src/pages/components/code-highlighted.mdx
+++ b/packages/kumo-docs-astro/src/pages/components/code-highlighted.mdx
@@ -16,6 +16,7 @@ import {
CodeHighlightedHighlightLinesDemo,
CodeHighlightedLineNumbersDemo,
CodeHighlightedCopyButtonDemo,
+ CodeHighlightedPlainDemo,
CodeHighlightedFullFeaturedDemo,
CodeHighlightedSharedProviderDemo,
CodeHighlightedCssDemo,
@@ -173,6 +174,16 @@ export function App() {
+### Plain
+
+
+ Use `variant="plain"` to remove the outer surface, border, and rounded corners
+ when embedding highlighted code inside another container.
+
+
+
+
+
### Full Featured
Combine all features for a complete code display experience.
@@ -568,6 +579,16 @@ import { ShikiProvider, CodeHighlighted } from "@cloudflare/kumo/code";
Language identifier (must be in provider's languages)
+
+
+ variant
+ |
+
+ "default" | "plain"
+ |
+ No |
+ Visual style of the code block |
+
showLineNumbers
diff --git a/packages/kumo/src/code/code-highlighted.tsx b/packages/kumo/src/code/code-highlighted.tsx
index 05b027816..8939b07d8 100644
--- a/packages/kumo/src/code/code-highlighted.tsx
+++ b/packages/kumo/src/code/code-highlighted.tsx
@@ -34,6 +34,7 @@ import type { CodeHighlightedProps } from "./types";
export function CodeHighlighted({
code,
lang,
+ variant = "default",
showLineNumbers = false,
highlightLines,
showCopyButton = false,
@@ -76,17 +77,45 @@ export function CodeHighlighted({
// Container styles - use flex layout for single-line with copy button
// Includes defensive resets (m-0, p-0) to prevent global CSS pollution
const containerClasses = cn(
- "group relative m-0 w-full min-w-0 rounded-md border border-kumo-fill bg-kumo-base p-0",
+ "group relative m-0 w-full min-w-0 p-0",
+ variant === "plain"
+ ? "rounded-none border-0 bg-transparent"
+ : "rounded-md border border-kumo-fill bg-kumo-base",
showCopyButton && isSingleLine && "flex items-center",
className,
);
+ const fallbackCodeClasses = cn(
+ // Prevent global pre styles from affecting the loading and error states.
+ "!m-0 min-w-0 flex-1 overflow-x-auto",
+ "font-mono text-sm leading-relaxed text-kumo-subtle",
+ variant === "plain" ? "!p-0" : "!p-4",
+ );
+
+ const highlightedCodeClasses = cn(
+ "kumo-shiki",
+ // Reset the code element generated by Shiki.
+ "[&_code]:!m-0 [&_code]:!border-0 [&_code]:!bg-transparent [&_code]:!p-0",
+ // Let the Kumo container provide the frame and background.
+ "[&>pre]:!m-0 [&>pre]:!rounded-none [&>pre]:!border-0 [&>pre]:!bg-transparent",
+ "[&>pre]:font-mono [&>pre]:text-sm [&>pre]:leading-relaxed",
+ variant === "plain" ? "[&>pre]:!p-0" : "[&>pre]:!p-4",
+ // Highlighted lines normally extend into the default padding. A plain
+ // block has no padding, so keep its highlights within the content width.
+ variant === "plain" &&
+ "[&_.line-highlighted]:!m-0 [&_.line-highlighted]:!w-full [&_.line-highlighted]:!px-0",
+ );
+
// Copy button - inline for single-line, absolute for multi-line
// Hidden until hover (or when showing "Copied!" feedback)
const copyButton = showCopyButton ? (
@@ -105,7 +134,10 @@ export function CodeHighlighted({
const lineNumbers =
showLineNumbers && !isSingleLine ? (
{Array.from({ length: lineCount }, (_, i) => (
@@ -133,13 +165,13 @@ export function CodeHighlighted({
{lineNumbers && (
)}
{!lineNumbers && (
-
+
{code}
)}
@@ -156,7 +188,7 @@ export function CodeHighlighted({
{lineNumbers}
{
expect(mockHighlighter.codeToHtml).toHaveBeenCalledTimes(1);
});
+ it("renders the plain CodeHighlighted variant without a frame", () => {
+ const { container } = render(
+
+
+ ,
+ );
+
+ const classList = container.firstElementChild?.classList;
+ expect(classList?.contains("rounded-none")).toBe(true);
+ expect(classList?.contains("border-0")).toBe(true);
+ expect(classList?.contains("bg-transparent")).toBe(true);
+ expect(classList?.contains("rounded-md")).toBe(false);
+ expect(classList?.contains("border-kumo-fill")).toBe(false);
+ expect(classList?.contains("bg-kumo-base")).toBe(false);
+ expect(container.querySelector("pre")?.classList.contains("!p-0")).toBe(
+ true,
+ );
+
+ const copyButtonContainer = screen.getByRole("button", {
+ name: "Copy",
+ }).parentElement;
+ expect(copyButtonContainer?.classList.contains("top-0")).toBe(true);
+ expect(copyButtonContainer?.classList.contains("right-0")).toBe(true);
+ expect(copyButtonContainer?.classList.contains("top-2")).toBe(false);
+ expect(copyButtonContainer?.classList.contains("right-2")).toBe(false);
+ });
+
+ it("removes Shiki pre padding from the plain CodeHighlighted variant", async () => {
+ mockHighlighter.codeToHtml.mockReturnValue(
+ ' const x = 1;\nconst y = 2;
',
+ );
+
+ const { container } = render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(container.querySelector(".kumo-shiki")).not.toBeNull();
+ });
+
+ const classList = container.querySelector(".kumo-shiki")?.classList;
+ expect(classList?.contains("[&>pre]:!p-0")).toBe(true);
+ expect(classList?.contains("[&>pre]:!p-4")).toBe(false);
+
+ const lineNumberClasses =
+ container.querySelector(".kumo-line-numbers")?.classList;
+ expect(lineNumberClasses?.contains("py-0")).toBe(true);
+ expect(lineNumberClasses?.contains("py-4")).toBe(false);
+ });
+
+ it("does not extend highlighted lines beyond a plain code block", async () => {
+ mockHighlighter.codeToHtml.mockReturnValue(
+ ' const x = 1;
',
+ );
+
+ const { container } = render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(container.querySelector(".line-highlighted")).not.toBeNull();
+ });
+
+ const classList = container.querySelector(".kumo-shiki")?.classList;
+ expect(classList?.contains("[&_.line-highlighted]:!m-0")).toBe(true);
+ expect(classList?.contains("[&_.line-highlighted]:!w-full")).toBe(true);
+ expect(classList?.contains("[&_.line-highlighted]:!px-0")).toBe(true);
+ });
+
it("exposes normalized languages in context so hook alias resolution works end-to-end", async () => {
// This is the integration test for the bug where the context stores raw
// aliases (e.g., ["js", "ts"]) but the hook normalizes to canonical names
diff --git a/packages/kumo/src/code/types.ts b/packages/kumo/src/code/types.ts
index 4ae708824..a315c0e5f 100644
--- a/packages/kumo/src/code/types.ts
+++ b/packages/kumo/src/code/types.ts
@@ -135,6 +135,8 @@ export interface UseShikiHighlighterResult {
/**
* Props for CodeHighlighted component.
*/
+export type CodeHighlightedVariant = "default" | "plain";
+
export interface CodeHighlightedProps {
/** Source code to display */
code: string;
@@ -155,9 +157,6 @@ export interface CodeHighlightedProps {
*/
highlightLines?: number[];
- /** Show copy-to-clipboard button */
- showCopyButton?: boolean;
-
/**
* Override provider labels for this instance.
* @example { copy: "Copy code", copied: "Done!" }
@@ -166,6 +165,12 @@ export interface CodeHighlightedProps {
/** Additional CSS classes */
className?: string;
+
+ /** Code block appearance. @default "default" */
+ variant?: CodeHighlightedVariant;
+
+ /** Show copy-to-clipboard button */
+ showCopyButton?: boolean;
}
// Re-export for backwards compatibility (deprecated, use SupportedLanguage instead)
|