@kushagradhawan/kookie-blocks 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/components.css +100 -0
  2. package/dist/cjs/components/code/CodeBlock.d.ts +4 -0
  3. package/dist/cjs/components/code/CodeBlock.d.ts.map +1 -0
  4. package/dist/cjs/components/code/CodeBlock.js +2 -0
  5. package/dist/cjs/components/code/CodeBlock.js.map +7 -0
  6. package/dist/cjs/components/code/CopyButton.d.ts +7 -0
  7. package/dist/cjs/components/code/CopyButton.d.ts.map +1 -0
  8. package/dist/cjs/components/code/CopyButton.js +2 -0
  9. package/dist/cjs/components/code/CopyButton.js.map +7 -0
  10. package/dist/cjs/components/code/LanguageBadge.d.ts +7 -0
  11. package/dist/cjs/components/code/LanguageBadge.d.ts.map +1 -0
  12. package/dist/cjs/components/code/LanguageBadge.js +2 -0
  13. package/dist/cjs/components/code/LanguageBadge.js.map +7 -0
  14. package/dist/cjs/components/code/PreviewSection.d.ts +16 -0
  15. package/dist/cjs/components/code/PreviewSection.d.ts.map +1 -0
  16. package/dist/cjs/components/code/PreviewSection.js +2 -0
  17. package/dist/cjs/components/code/PreviewSection.js.map +7 -0
  18. package/dist/cjs/components/code/SyntaxHighlighter.d.ts +10 -0
  19. package/dist/cjs/components/code/SyntaxHighlighter.d.ts.map +1 -0
  20. package/dist/cjs/components/code/SyntaxHighlighter.js +2 -0
  21. package/dist/cjs/components/code/SyntaxHighlighter.js.map +7 -0
  22. package/dist/cjs/components/code/index.d.ts +3 -0
  23. package/dist/cjs/components/code/index.d.ts.map +1 -0
  24. package/dist/cjs/components/code/index.js +2 -0
  25. package/dist/cjs/components/code/index.js.map +7 -0
  26. package/dist/cjs/components/code/types.d.ts +24 -0
  27. package/dist/cjs/components/code/types.d.ts.map +1 -0
  28. package/dist/cjs/components/code/types.js +2 -0
  29. package/dist/cjs/components/code/types.js.map +7 -0
  30. package/dist/cjs/components/index.d.ts +1 -0
  31. package/dist/cjs/components/index.d.ts.map +1 -1
  32. package/dist/cjs/components/index.js +1 -1
  33. package/dist/cjs/components/index.js.map +2 -2
  34. package/dist/esm/components/code/CodeBlock.d.ts +4 -0
  35. package/dist/esm/components/code/CodeBlock.d.ts.map +1 -0
  36. package/dist/esm/components/code/CodeBlock.js +2 -0
  37. package/dist/esm/components/code/CodeBlock.js.map +7 -0
  38. package/dist/esm/components/code/CopyButton.d.ts +7 -0
  39. package/dist/esm/components/code/CopyButton.d.ts.map +1 -0
  40. package/dist/esm/components/code/CopyButton.js +2 -0
  41. package/dist/esm/components/code/CopyButton.js.map +7 -0
  42. package/dist/esm/components/code/LanguageBadge.d.ts +7 -0
  43. package/dist/esm/components/code/LanguageBadge.d.ts.map +1 -0
  44. package/dist/esm/components/code/LanguageBadge.js +2 -0
  45. package/dist/esm/components/code/LanguageBadge.js.map +7 -0
  46. package/dist/esm/components/code/PreviewSection.d.ts +16 -0
  47. package/dist/esm/components/code/PreviewSection.d.ts.map +1 -0
  48. package/dist/esm/components/code/PreviewSection.js +2 -0
  49. package/dist/esm/components/code/PreviewSection.js.map +7 -0
  50. package/dist/esm/components/code/SyntaxHighlighter.d.ts +10 -0
  51. package/dist/esm/components/code/SyntaxHighlighter.d.ts.map +1 -0
  52. package/dist/esm/components/code/SyntaxHighlighter.js +2 -0
  53. package/dist/esm/components/code/SyntaxHighlighter.js.map +7 -0
  54. package/dist/esm/components/code/index.d.ts +3 -0
  55. package/dist/esm/components/code/index.d.ts.map +1 -0
  56. package/dist/esm/components/code/index.js +2 -0
  57. package/dist/esm/components/code/index.js.map +7 -0
  58. package/dist/esm/components/code/types.d.ts +24 -0
  59. package/dist/esm/components/code/types.d.ts.map +1 -0
  60. package/dist/esm/components/code/types.js +1 -0
  61. package/dist/esm/components/code/types.js.map +7 -0
  62. package/dist/esm/components/index.d.ts +1 -0
  63. package/dist/esm/components/index.d.ts.map +1 -1
  64. package/dist/esm/components/index.js +1 -1
  65. package/dist/esm/components/index.js.map +2 -2
  66. package/package.json +6 -5
  67. package/src/components/code/CodeBlock.tsx +119 -0
  68. package/src/components/code/CopyButton.tsx +46 -0
  69. package/src/components/code/LanguageBadge.tsx +33 -0
  70. package/src/components/code/PreviewSection.tsx +52 -0
  71. package/src/components/code/SyntaxHighlighter.tsx +55 -0
  72. package/src/components/code/index.ts +3 -0
  73. package/src/components/code/types.ts +32 -0
  74. package/src/components/index.css +88 -0
  75. package/src/components/index.ts +1 -0
  76. package/styles.css +82 -0
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from "react";
2
+ export interface CodeBlockProps {
3
+ children?: ReactNode;
4
+ code?: string;
5
+ language?: string;
6
+ preview?: ReactNode;
7
+ showCopy?: boolean;
8
+ showLanguage?: boolean;
9
+ file?: string;
10
+ collapsible?: boolean;
11
+ collapsedHeight?: number;
12
+ background?: "none" | "dots" | string;
13
+ backgroundProps?: {
14
+ dotSize?: number;
15
+ color?: string;
16
+ backgroundColor?: string;
17
+ height?: string;
18
+ width?: string;
19
+ radius?: string;
20
+ };
21
+ lightTheme?: string;
22
+ darkTheme?: string;
23
+ }
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/code/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,cAAc;IAE7B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,eAAe,CAAC,EAAE;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAGF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -1,2 +1,3 @@
1
+ export * from './code';
1
2
  export * from './hero';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC"}
@@ -1,2 +1,2 @@
1
- export*from"./hero";
1
+ export*from"./code";export*from"./hero";
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/index.ts"],
4
- "sourcesContent": ["export * from './hero';\n"],
5
- "mappings": "AAAA,WAAc",
4
+ "sourcesContent": ["export * from './code';\nexport * from './hero';\n"],
5
+ "mappings": "AAAA,WAAc,SACd,WAAc",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kushagradhawan/kookie-blocks",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/KushagraDhawan1997/kookie-blocks.git"
@@ -91,13 +91,14 @@
91
91
  "prepublishOnly": "pnpm lint"
92
92
  },
93
93
  "dependencies": {
94
+ "@hugeicons/core-free-icons": "^2.0.0",
95
+ "@hugeicons/react": "^1.1.1",
94
96
  "@kushagradhawan/kookie-ui": "latest",
95
- "react": "^19.0.0",
96
- "react-dom": "^19.0.0"
97
+ "shiki": "^1.24.2"
97
98
  },
98
99
  "peerDependencies": {
99
- "react": ">=18.0.0",
100
- "react-dom": ">=18.0.0"
100
+ "react": "^18.0.0 || ^19.0.0",
101
+ "react-dom": "^18.0.0 || ^19.0.0"
101
102
  },
102
103
  "devDependencies": {
103
104
  "@eslint/js": "^9.18.0",
@@ -0,0 +1,119 @@
1
+ import React, { type ReactNode } from "react";
2
+ import { Card, Flex } from "@kushagradhawan/kookie-ui";
3
+ import { SyntaxHighlighter } from "./SyntaxHighlighter";
4
+ import { CopyButton } from "./CopyButton";
5
+ import { LanguageBadge } from "./LanguageBadge";
6
+ import { PreviewSection } from "./PreviewSection";
7
+ import type { CodeBlockProps } from "./types";
8
+
9
+ function extractLanguageFromChildren(children?: ReactNode): string {
10
+ if (!children) return "text";
11
+
12
+ // Try to extract language from pre > code className
13
+ if (typeof children === "object" && children !== null && "props" in children) {
14
+ const childProps = (children as any).props;
15
+
16
+ // If children is a <pre><code className="language-xxx">
17
+ if (childProps?.children && typeof childProps.children === "object") {
18
+ const codeProps = (childProps.children as any).props;
19
+ const className = codeProps?.className || "";
20
+ const match = className.match(/language-([\w-]+)/i);
21
+ if (match) return match[1];
22
+ }
23
+
24
+ // Direct className on children
25
+ const className = childProps?.className || "";
26
+ const match = className.match(/language-([\w-]+)/i);
27
+ if (match) return match[1];
28
+ }
29
+
30
+ return "text";
31
+ }
32
+
33
+ function extractCodeFromChildren(children?: ReactNode): string {
34
+ if (!children) return "";
35
+
36
+ // Try to extract text from pre > code structure
37
+ if (typeof children === "object" && children !== null && "props" in children) {
38
+ const childProps = (children as any).props;
39
+
40
+ // If children is <pre><code>...</code></pre>
41
+ if (childProps?.children && typeof childProps.children === "object") {
42
+ const codeProps = (childProps.children as any).props;
43
+ const codeChildren = codeProps?.children;
44
+
45
+ if (typeof codeChildren === "string") {
46
+ return codeChildren;
47
+ }
48
+ }
49
+
50
+ // Direct text content
51
+ if (typeof childProps?.children === "string") {
52
+ return childProps.children;
53
+ }
54
+ }
55
+
56
+ if (typeof children === "string") {
57
+ return children;
58
+ }
59
+
60
+ return "";
61
+ }
62
+
63
+ export function CodeBlock({
64
+ children,
65
+ code,
66
+ language,
67
+ preview,
68
+ showCopy = true,
69
+ showLanguage = true,
70
+ lightTheme,
71
+ darkTheme,
72
+ background,
73
+ backgroundProps,
74
+ }: CodeBlockProps) {
75
+ // Determine the code and language to display
76
+ let displayCode = code;
77
+ let displayLanguage = language;
78
+
79
+ // If children are provided (pre-highlighted from MDX), extract code and language
80
+ if (children && !code) {
81
+ displayCode = extractCodeFromChildren(children);
82
+ displayLanguage = language || extractLanguageFromChildren(children);
83
+ }
84
+
85
+ // Default language
86
+ if (!displayLanguage) {
87
+ displayLanguage = "text";
88
+ }
89
+
90
+ // If no code to display, render nothing
91
+ if (!displayCode && !children) {
92
+ return null;
93
+ }
94
+
95
+ return (
96
+ <Flex direction="column" className="code-block-wrapper" style={{ minWidth: 0 }} my="2">
97
+ {preview && (
98
+ <PreviewSection background={background} backgroundProps={backgroundProps}>
99
+ {preview}
100
+ </PreviewSection>
101
+ )}
102
+
103
+ <Card size="2" variant="soft">
104
+ <Flex gap="2" direction="column">
105
+ <Flex align="start" justify="between">
106
+ {showLanguage && <LanguageBadge language={displayLanguage} />}
107
+ {showCopy && displayCode && <CopyButton code={displayCode} />}
108
+ </Flex>
109
+
110
+ {/* If we have runtime code, use SyntaxHighlighter */}
111
+ {code && <SyntaxHighlighter code={code} language={displayLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />}
112
+
113
+ {/* If we have pre-highlighted children from MDX, render them */}
114
+ {children && !code && <div className="code-block-content">{children}</div>}
115
+ </Flex>
116
+ </Card>
117
+ </Flex>
118
+ );
119
+ }
@@ -0,0 +1,46 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from "react";
2
+ import { Button } from "@kushagradhawan/kookie-ui";
3
+ import { HugeiconsIcon } from "@hugeicons/react";
4
+ import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons";
5
+
6
+ interface CopyButtonProps {
7
+ code: string;
8
+ }
9
+
10
+ export function CopyButton({ code }: CopyButtonProps) {
11
+ const [copied, setCopied] = useState(false);
12
+ const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
13
+
14
+ useEffect(() => {
15
+ return () => {
16
+ if (resetTimeoutRef.current) {
17
+ clearTimeout(resetTimeoutRef.current);
18
+ }
19
+ };
20
+ }, []);
21
+
22
+ const handleCopy = useCallback(async () => {
23
+ if (!code.trim()) return;
24
+
25
+ try {
26
+ await navigator.clipboard.writeText(code);
27
+ setCopied(true);
28
+
29
+ if (resetTimeoutRef.current) {
30
+ clearTimeout(resetTimeoutRef.current);
31
+ }
32
+
33
+ resetTimeoutRef.current = setTimeout(() => {
34
+ setCopied(false);
35
+ }, 2000);
36
+ } catch {
37
+ // Silently fail
38
+ }
39
+ }, [code]);
40
+
41
+ return (
42
+ <Button size="2" tooltip={copied ? "Copied" : "Copy code"} variant="ghost" onClick={handleCopy} aria-label={copied ? "Copied" : "Copy code"}>
43
+ <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy
44
+ </Button>
45
+ );
46
+ }
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { Code } from "@kushagradhawan/kookie-ui";
3
+
4
+ interface LanguageBadgeProps {
5
+ language: string;
6
+ }
7
+
8
+ function formatLanguage(lang: string): string {
9
+ const languageMap: Record<string, string> = {
10
+ js: "JS",
11
+ jsx: "JSX",
12
+ ts: "TS",
13
+ tsx: "TSX",
14
+ py: "Python",
15
+ rb: "Ruby",
16
+ sh: "Shell",
17
+ bash: "Bash",
18
+ text: "plaintext",
19
+ };
20
+
21
+ return languageMap[lang.toLowerCase()] || lang;
22
+ }
23
+
24
+ export function LanguageBadge({ language }: LanguageBadgeProps) {
25
+ const displayLanguage = formatLanguage(language);
26
+
27
+ return (
28
+ <Code size="1" color="gray" highContrast>
29
+ {displayLanguage}
30
+ </Code>
31
+ );
32
+ }
33
+
@@ -0,0 +1,52 @@
1
+ import React, { type ReactNode } from "react";
2
+ import { Box } from "@kushagradhawan/kookie-ui";
3
+
4
+ interface PreviewSectionProps {
5
+ children: ReactNode;
6
+ background?: "none" | "dots" | string;
7
+ backgroundProps?: {
8
+ dotSize?: number;
9
+ color?: string;
10
+ backgroundColor?: string;
11
+ height?: string;
12
+ width?: string;
13
+ radius?: string;
14
+ };
15
+ }
16
+
17
+ export function PreviewSection({ children, background = "none", backgroundProps = {} }: PreviewSectionProps) {
18
+ const { dotSize = 2, color = "#d4d4d8", backgroundColor = "transparent", height = "auto", width = "100%", radius = "var(--radius-3)" } = backgroundProps;
19
+
20
+ const backgroundStyle =
21
+ background === "dots"
22
+ ? {
23
+ backgroundImage: `radial-gradient(circle, ${color} ${dotSize}px, ${backgroundColor} ${dotSize}px)`,
24
+ backgroundSize: "20px 20px",
25
+ }
26
+ : background !== "none"
27
+ ? {
28
+ backgroundImage: `url(${background})`,
29
+ backgroundSize: "cover",
30
+ backgroundPosition: "center",
31
+ }
32
+ : {};
33
+
34
+ return (
35
+ <Box
36
+ p="4"
37
+ style={{
38
+ ...backgroundStyle,
39
+ height,
40
+ width,
41
+ borderRadius: radius,
42
+ display: "flex",
43
+ alignItems: "center",
44
+ justifyContent: "center",
45
+ minHeight: "200px",
46
+ }}
47
+ >
48
+ {children}
49
+ </Box>
50
+ );
51
+ }
52
+
@@ -0,0 +1,55 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { codeToHtml } from "shiki";
3
+ import { Box, Code } from "@kushagradhawan/kookie-ui";
4
+
5
+ interface SyntaxHighlighterProps {
6
+ code: string;
7
+ language: string;
8
+ lightTheme?: string;
9
+ darkTheme?: string;
10
+ }
11
+
12
+ const DEFAULT_LIGHT_THEME = "one-light";
13
+ const DEFAULT_DARK_THEME = "one-dark-pro";
14
+
15
+ export function SyntaxHighlighter({ code, language, lightTheme = DEFAULT_LIGHT_THEME, darkTheme = DEFAULT_DARK_THEME }: SyntaxHighlighterProps) {
16
+ const [highlighted, setHighlighted] = useState<string | null>(null);
17
+
18
+ useEffect(() => {
19
+ let cancelled = false;
20
+
21
+ codeToHtml(code, {
22
+ lang: language,
23
+ themes: {
24
+ light: lightTheme,
25
+ dark: darkTheme,
26
+ },
27
+ defaultColor: false,
28
+ })
29
+ .then((html) => {
30
+ if (!cancelled) {
31
+ setHighlighted(html);
32
+ }
33
+ })
34
+ .catch(() => {
35
+ if (!cancelled) {
36
+ setHighlighted(null);
37
+ }
38
+ });
39
+
40
+ return () => {
41
+ cancelled = true;
42
+ };
43
+ }, [code, language, lightTheme, darkTheme]);
44
+
45
+ if (!highlighted) {
46
+ return (
47
+ <pre className="code-block-content">
48
+ <Code size="3">{code}</Code>
49
+ </pre>
50
+ );
51
+ }
52
+
53
+ return <Box className="code-block-content" width="100%" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />;
54
+ }
55
+
@@ -0,0 +1,3 @@
1
+ // Public API - only CodeBlock is exported
2
+ export { CodeBlock } from "./CodeBlock";
3
+ export type { CodeBlockProps } from "./types";
@@ -0,0 +1,32 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export interface CodeBlockProps {
4
+ // Content modes (mutually exclusive)
5
+ children?: ReactNode; // Pre-highlighted HTML from build-time (MDX)
6
+ code?: string; // Raw code to highlight at runtime
7
+ language?: string; // Language for runtime highlighting
8
+
9
+ // Optional features
10
+ preview?: ReactNode; // Preview component (for docs)
11
+ showCopy?: boolean; // Default: true
12
+ showLanguage?: boolean; // Default: true
13
+ file?: string; // File path label
14
+ collapsible?: boolean; // Enable expand/collapse
15
+ collapsedHeight?: number; // Default: 360px
16
+
17
+ // Preview styling (for kookie-ui docs)
18
+ background?: "none" | "dots" | string; // Background type
19
+ backgroundProps?: {
20
+ dotSize?: number;
21
+ color?: string;
22
+ backgroundColor?: string;
23
+ height?: string;
24
+ width?: string;
25
+ radius?: string;
26
+ };
27
+
28
+ // Theme
29
+ lightTheme?: string; // Default: 'one-light'
30
+ darkTheme?: string; // Default: 'one-dark-pro'
31
+ }
32
+
@@ -1 +1,89 @@
1
1
  /* Components CSS aggregator for Kookie Blocks (e.g., hero variants if styled). */
2
+
3
+ /* ============================================
4
+ Code Block Component Styles
5
+ ============================================ */
6
+
7
+ /* Code block wrapper - constrain width in flex containers */
8
+ .code-block-wrapper {
9
+ min-width: 0;
10
+ max-width: 100%;
11
+ width: 100%;
12
+ overflow: hidden;
13
+ }
14
+
15
+ /* Code block content (the pre part, below the header) */
16
+ .code-block-content {
17
+ width: 100%;
18
+ min-width: 0;
19
+ }
20
+
21
+ .code-block-wrapper pre,
22
+ .code-block-wrapper .code-block-content pre {
23
+ overflow-x: auto;
24
+ max-width: 100%;
25
+ min-width: 0;
26
+ margin: 0;
27
+ width: 100%;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ /* Ensure Shiki-generated pre elements fill width */
32
+ .code-block-content > pre {
33
+ width: 100% !important;
34
+ min-width: 0;
35
+ }
36
+
37
+ /* Code elements inside pre */
38
+ .code-block-wrapper pre code,
39
+ .code-block-wrapper .code-block-content pre code {
40
+ font-family: var(--font-mono);
41
+ font-size: var(--font-size-2);
42
+ line-height: 1.75;
43
+ background: none !important;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: var(--space-1);
47
+ counter-reset: line;
48
+ }
49
+
50
+ /* Shiki line spans */
51
+ .code-block-wrapper pre code .line,
52
+ .code-block-wrapper .code-block-content pre code .line {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 0;
56
+ }
57
+
58
+ /* Line numbers */
59
+ .code-block-wrapper pre code .line::before,
60
+ .code-block-wrapper .code-block-content pre code .line::before {
61
+ counter-increment: line;
62
+ content: counter(line);
63
+ display: inline-block;
64
+ min-width: 2ch;
65
+ text-align: right;
66
+ font-size: var(--font-size-1);
67
+ color: var(--gray-a9);
68
+ user-select: none;
69
+ flex-shrink: 0;
70
+ margin-right: var(--space-4);
71
+ }
72
+
73
+ /* Default to light theme for all tokens (codeToHtml with defaultColor: false) */
74
+ .code-block-wrapper pre.shiki span {
75
+ color: var(--shiki-light) !important;
76
+ font-style: var(--shiki-light-font-style);
77
+ font-weight: var(--shiki-light-font-weight);
78
+ text-decoration: var(--shiki-light-text-decoration);
79
+ }
80
+
81
+ /* Override with dark theme colors when inside a dark context */
82
+ .dark .code-block-wrapper pre.shiki span,
83
+ .dark-theme .code-block-wrapper pre.shiki span,
84
+ [data-appearance="dark"] .code-block-wrapper pre.shiki span {
85
+ color: var(--shiki-dark) !important;
86
+ font-style: var(--shiki-dark-font-style);
87
+ font-weight: var(--shiki-dark-font-weight);
88
+ text-decoration: var(--shiki-dark-text-decoration);
89
+ }
@@ -1 +1,2 @@
1
+ export * from './code';
1
2
  export * from './hero';
package/styles.css CHANGED
@@ -1,2 +1,84 @@
1
1
  /* Kookie Blocks base styles. Keep minimal; rely on Kookie UI tokens/utilities. */
2
2
  /* Components CSS aggregator for Kookie Blocks (e.g., hero variants if styled). */
3
+ /* ============================================
4
+ Code Block Component Styles
5
+ ============================================ */
6
+ /* Code block wrapper - constrain width in flex containers */
7
+ .code-block-wrapper {
8
+ min-width: 0;
9
+ max-width: 100%;
10
+ width: 100%;
11
+ overflow: hidden;
12
+ }
13
+ /* Code block content (the pre part, below the header) */
14
+ .code-block-content {
15
+ width: 100%;
16
+ min-width: 0;
17
+ }
18
+ .code-block-wrapper pre,
19
+ .code-block-wrapper .code-block-content pre {
20
+ overflow-x: auto;
21
+ max-width: 100%;
22
+ min-width: 0;
23
+ margin: 0;
24
+ width: 100%;
25
+ box-sizing: border-box;
26
+ }
27
+ /* Ensure Shiki-generated pre elements fill width */
28
+ .code-block-content > pre {
29
+ width: 100% !important;
30
+ min-width: 0;
31
+ }
32
+ /* Code elements inside pre */
33
+ .code-block-wrapper pre code,
34
+ .code-block-wrapper .code-block-content pre code {
35
+ font-family: var(--font-mono);
36
+ font-size: var(--font-size-2);
37
+ line-height: 1.75;
38
+ background: none !important;
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: var(--space-1);
42
+ counter-reset: line;
43
+ }
44
+ /* Shiki line spans */
45
+ .code-block-wrapper pre code .line,
46
+ .code-block-wrapper .code-block-content pre code .line {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 0;
50
+ }
51
+ /* Line numbers */
52
+ .code-block-wrapper pre code .line::before,
53
+ .code-block-wrapper .code-block-content pre code .line::before {
54
+ counter-increment: line;
55
+ content: counter(line);
56
+ display: inline-block;
57
+ min-width: 2ch;
58
+ text-align: right;
59
+ font-size: var(--font-size-1);
60
+ color: var(--gray-a9);
61
+ -webkit-user-select: none;
62
+ -moz-user-select: none;
63
+ user-select: none;
64
+ flex-shrink: 0;
65
+ margin-right: var(--space-4);
66
+ }
67
+ /* Default to light theme for all tokens (codeToHtml with defaultColor: false) */
68
+ .code-block-wrapper pre.shiki span {
69
+ color: var(--shiki-light) !important;
70
+ font-style: var(--shiki-light-font-style);
71
+ font-weight: var(--shiki-light-font-weight);
72
+ -webkit-text-decoration: var(--shiki-light-text-decoration);
73
+ text-decoration: var(--shiki-light-text-decoration);
74
+ }
75
+ /* Override with dark theme colors when inside a dark context */
76
+ .dark .code-block-wrapper pre.shiki span,
77
+ .dark-theme .code-block-wrapper pre.shiki span,
78
+ [data-appearance="dark"] .code-block-wrapper pre.shiki span {
79
+ color: var(--shiki-dark) !important;
80
+ font-style: var(--shiki-dark-font-style);
81
+ font-weight: var(--shiki-dark-font-weight);
82
+ -webkit-text-decoration: var(--shiki-dark-text-decoration);
83
+ text-decoration: var(--shiki-dark-text-decoration);
84
+ }