-/* Chroma Syntax Highlighting Theme */
-/* Dark/Light theme support with custom properties */
+/* === Chroma Custom — Catppuccin Macchiato === */
:root {
- /* Dark theme colors */
- --chroma-bg-dark: #0c1520;
- --chroma-bg-light: #f0f4f8;
- --chroma-text-dark: #c4d6e8;
- --chroma-text-light: #0d1b2a;
- --chroma-keyword: #a855f7;
- --chroma-string: #00ff88;
- --chroma-number: #38bdf8;
- --chroma-comment: #7a9bb8;
- --chroma-error: #ff6b6b;
-}
-
-/* Default dark theme for .highlight */
-.highlight {
- background-color: var(--chroma-bg-dark);
- color: var(--chroma-text-dark);
- padding: 1rem;
- border-radius: 0.375rem;
+ /* Catppuccin Macchiato palette */
+ --ctp-base: #24273a;
+ --ctp-surface0: #363a4f;
+ --ctp-surface1: #494d64;
+ --ctp-overlay0: #6e738d;
+ --ctp-text: #cad3f5;
+ --ctp-subtext1: #b8c0e0;
+ --ctp-lavender: #b7bdf8;
+ --ctp-blue: #8aadf4;
+ --ctp-sapphire: #7dc4e4;
+ --ctp-sky: #91d7e3;
+ --ctp-teal: #8bd5ca;
+ --ctp-green: #a6da95;
+ --ctp-yellow: #eed49f;
+ --ctp-peach: #f5a97f;
+ --ctp-maroon: #ee99a0;
+ --ctp-red: #ed8796;
+ --ctp-mauve: #c6a0f6;
+ --ctp-pink: #f5bde6;
+}
+
+/* === Code block wrapper and header bar === */
+
+.code-block-wrapper {
+ margin: 1.5rem 0;
+ border-radius: 0.5rem;
+ overflow: hidden;
+ border: 1px solid var(--border);
+}
+
+.code-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.375rem 0.75rem;
+ background-color: var(--ctp-surface0);
+ border-bottom: 1px solid var(--ctp-surface1);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 0.75rem;
+}
+
+.code-lang-label {
+ color: var(--ctp-subtext1);
+ letter-spacing: 0.05em;
+}
+
+.code-copy-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.code-copy-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 0.25rem;
+ border-radius: 0.25rem;
+ color: var(--ctp-overlay0);
+ transition: color 0.15s ease, background 0.15s ease;
+ line-height: 1;
+}
+
+.code-copy-btn:hover {
+ color: var(--ctp-text);
+ background: rgba(202, 211, 245, 0.08);
+}
+
+.code-copy-btn:focus-visible {
+ outline: 2px solid var(--ctp-lavender);
+ outline-offset: 2px;
+}
+
+.code-copy-btn [data-feather],
+.code-copy-btn svg {
+ width: 14px !important;
+ height: 14px !important;
+ stroke-width: 2px;
+}
+
+.code-copy-btn .hidden {
+ display: none;
+}
+
+.code-copy-btn.is-copied {
+ color: var(--ctp-green);
+}
+
+/* === Code body === */
+
+.code-body {
overflow-x: auto;
- font-size: 0.875rem;
- line-height: 1.5;
-}
-
-/* Light theme override */
-html.theme-light .highlight {
- background-color: var(--chroma-bg-light);
- color: var(--chroma-text-light);
-}
-
-/* Light theme token colors for proper contrast */
-html.theme-light .highlight .k,
-html.theme-light .highlight .kc,
-html.theme-light .highlight .kd,
-html.theme-light .highlight .kn,
-html.theme-light .highlight .kp,
-html.theme-light .highlight .kr,
-html.theme-light .highlight .kt {
- color: #7c3aed;
-}
-
-html.theme-light .highlight .s,
-html.theme-light .highlight .sb,
-html.theme-light .highlight .sc,
-html.theme-light .highlight .sd,
-html.theme-light .highlight .s1,
-html.theme-light .highlight .s2,
-html.theme-light .highlight .se,
-html.theme-light .highlight .sh,
-html.theme-light .highlight .si,
-html.theme-light .highlight .sx {
- color: #059669;
-}
-
-html.theme-light .highlight .m,
-html.theme-light .highlight .mb,
-html.theme-light .highlight .mf,
-html.theme-light .highlight .mh,
-html.theme-light .highlight .mi,
-html.theme-light .highlight .il,
-html.theme-light .highlight .mo {
- color: #0284c7;
}
-html.theme-light .highlight .c,
-html.theme-light .highlight .c1,
-html.theme-light .highlight .cm {
- color: #6888a8;
+.code-body .highlight {
+ margin: 0;
+ border-radius: 0;
+ border: none;
+ background-color: var(--ctp-base);
}
-/* Keyword tokens - purple */
+/* === Reset conflicts with main.css base styles === */
+
+.code-block-wrapper pre,
+.prose .code-block-wrapper pre,
+.prose-invert .code-block-wrapper pre {
+ margin: 0;
+ padding: 0;
+ background: transparent;
+ border: none;
+ border-radius: 0;
+ overflow-x: visible;
+}
+
+/* === Chroma table layout (lineNumbersInTable = true) === */
+
+.highlight table {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+.highlight td {
+ padding: 0;
+ vertical-align: top;
+}
+
+/* Line number column — not selectable */
+/* color: --ctp-overlay0 (#6e738d) on --ctp-surface0 (#363a4f) = ~3.5:1 */
+.highlight .lnt,
+.highlight .ln {
+ padding: 0.875rem 0.75rem 0.875rem 1rem;
+ color: var(--ctp-overlay0);
+ user-select: none;
+ -webkit-user-select: none;
+ min-width: 2.5rem;
+ text-align: right;
+ border-right: 1px solid var(--ctp-surface1);
+ font-size: 0.8125rem;
+}
+
+.highlight .lntd:first-child {
+ background-color: var(--ctp-surface0);
+}
+
+.highlight .lntd:last-child pre {
+ padding: 0.875rem 1rem;
+}
+
+/* === Syntax token colors (dark theme default) === */
+
+.highlight {
+ background-color: var(--ctp-base);
+ color: var(--ctp-text);
+}
+
+/* Keywords — mauve */
.highlight .k,
.highlight .kc,
.highlight .kd,
.highlight .kp,
.highlight .kr,
.highlight .kt {
- color: var(--chroma-keyword);
+ color: var(--ctp-mauve);
font-weight: 500;
}
-/* String tokens - green */
+/* Strings — green */
.highlight .s,
+.highlight .sa,
.highlight .sb,
.highlight .sc,
+.highlight .dl,
.highlight .sd,
.highlight .s1,
.highlight .s2,
.highlight .se,
.highlight .sh,
.highlight .si,
-.highlight .sx {
- color: var(--chroma-string);
+.highlight .sx,
+.highlight .sr,
+.highlight .ss {
+ color: var(--ctp-green);
}
-/* Number tokens - cyan */
+/* Numbers — peach */
.highlight .m,
.highlight .mb,
.highlight .mf,
.highlight .mi,
.highlight .il,
.highlight .mo {
- color: var(--chroma-number);
+ color: var(--ctp-peach);
}
-/* Comment tokens - gray, italic */
+/* Comments — overlay0, italic */
.highlight .c,
.highlight .c1,
-.highlight .cm {
- color: var(--chroma-comment);
+.highlight .cm,
+.highlight .cs,
+.highlight .cp,
+.highlight .cpf {
+ color: var(--ctp-overlay0);
+ font-style: italic;
+}
+
+/* Operators — sky */
+.highlight .o,
+.highlight .ow {
+ color: var(--ctp-sky);
+}
+
+/* Names / Identifiers */
+.highlight .n {
+ color: var(--ctp-text);
+}
+
+.highlight .na {
+ color: var(--ctp-yellow);
+}
+
+.highlight .nb {
+ color: var(--ctp-blue);
+}
+
+.highlight .nc {
+ color: var(--ctp-yellow);
+}
+
+.highlight .nd {
+ color: var(--ctp-pink);
+}
+
+.highlight .ne {
+ color: var(--ctp-maroon);
+}
+
+.highlight .nf,
+.highlight .fm {
+ color: var(--ctp-blue);
+}
+
+.highlight .ni {
+ color: var(--ctp-text);
+}
+
+.highlight .nl {
+ color: var(--ctp-teal);
+}
+
+.highlight .nn {
+ color: var(--ctp-yellow);
+}
+
+.highlight .nt {
+ color: var(--ctp-mauve);
+}
+
+.highlight .nv,
+.highlight .vc,
+.highlight .vg,
+.highlight .vi {
+ color: var(--ctp-text);
+}
+
+/* Punctuation */
+.highlight .p {
+ color: var(--ctp-subtext1);
+}
+
+/* Generic tokens (diff output etc.) */
+.highlight .gd {
+ color: var(--ctp-red);
+ background: rgba(237, 135, 150, 0.1);
+}
+
+.highlight .gi {
+ color: var(--ctp-green);
+ background: rgba(166, 218, 149, 0.1);
+}
+
+.highlight .gh {
+ color: var(--ctp-lavender);
+ font-weight: bold;
+}
+
+.highlight .gu {
+ color: var(--ctp-overlay0);
+}
+
+.highlight .ge {
font-style: italic;
}
-/* Name tokens - default text color */
-.highlight .n,
-.highlight .na,
-.highlight .nb,
-.highlight .nc,
-.highlight .no,
-.highlight .nd,
-.highlight .ni,
-.highlight .nl,
-.highlight .nn,
-.highlight .nt,
-.highlight .nv {
- color: inherit;
+.highlight .gs {
+ font-weight: bold;
}
-/* Error tokens - red */
+/* Error */
.highlight .err {
- color: var(--chroma-error);
+ color: var(--ctp-red);
}
-/* Line numbers styling */
-.highlight .ln {
- color: var(--chroma-comment);
- user-select: none;
- -webkit-user-select: none;
+/* === Light theme overrides (Catppuccin Latte) === */
+
+html.theme-light .code-block-wrapper {
+ border-color: #ccd0da;
}
-/* Inline code is styled in main.css - just reset within code blocks */
+html.theme-light .code-header {
+ background-color: #dce0ea;
+ border-bottom-color: #bcc0cc;
+}
-/* Code block styling for pre tag */
-pre {
- margin: 0;
+html.theme-light .code-lang-label {
+ color: #5c5f77;
}
-pre code {
- background-color: transparent;
- color: inherit;
- padding: 0;
+html.theme-light .code-copy-btn {
+ color: #6c6f85;
+}
+
+html.theme-light .code-copy-btn:hover {
+ color: #4c4f69;
+ background: rgba(76, 79, 105, 0.08);
+}
+
+html.theme-light .code-body .highlight {
+ background-color: #eff1f5;
+ color: #4c4f69;
+}
+
+html.theme-light .highlight .lntd:first-child {
+ background-color: #e6e9ef;
+}
+
+html.theme-light .highlight .lnt,
+html.theme-light .highlight .ln {
+ color: #bcc0cc;
+ border-right-color: #bcc0cc;
+}
+
+/* Comments must be darker to read on light bg */
+html.theme-light .highlight .c,
+html.theme-light .highlight .c1,
+html.theme-light .highlight .cm,
+html.theme-light .highlight .cs,
+html.theme-light .highlight .cp,
+html.theme-light .highlight .cpf {
+ color: #7c7f93;
+}
+
+/* Text defaults */
+html.theme-light .highlight .n {
+ color: #4c4f69;
+}
+
+html.theme-light .highlight .p {
+ color: #6c6f85;
}
--- /dev/null
+(function () {
+ var LANG_NAMES = {
+ bash: 'Shell', sh: 'Shell', shell: 'Shell', zsh: 'Shell',
+ js: 'JavaScript', javascript: 'JavaScript',
+ ts: 'TypeScript', typescript: 'TypeScript',
+ go: 'Go',
+ py: 'Python', python: 'Python',
+ rs: 'Rust', rust: 'Rust',
+ html: 'HTML',
+ css: 'CSS',
+ toml: 'TOML',
+ yaml: 'YAML', yml: 'YAML',
+ json: 'JSON',
+ sql: 'SQL',
+ md: 'Markdown', markdown: 'Markdown',
+ c: 'C',
+ cpp: 'C++', 'c++': 'C++',
+ java: 'Java',
+ php: 'PHP',
+ ruby: 'Ruby', rb: 'Ruby',
+ swift: 'Swift',
+ kotlin: 'Kotlin', kt: 'Kotlin',
+ dockerfile: 'Dockerfile',
+ makefile: 'Makefile',
+ text: 'Text', txt: 'Text',
+ };
+
+ function prettyName(lang) {
+ if (!lang) return '';
+ var key = lang.toLowerCase();
+ return LANG_NAMES[key] || (lang.charAt(0).toUpperCase() + lang.slice(1));
+ }
+
+ function getCodeText(wrapper) {
+ var el = wrapper.querySelector('.lntd:last-child code')
+ || wrapper.querySelector('.code-body code')
+ || wrapper.querySelector('.code-body pre');
+ return el ? el.innerText : '';
+ }
+
+ function initBlock(wrapper) {
+ var header = wrapper.querySelector('.code-header');
+ if (header) {
+ var label = wrapper.querySelector('.code-lang-label');
+ if (label) label.textContent = prettyName(header.getAttribute('data-lang') || '');
+ }
+
+ var btn = wrapper.querySelector('[data-copy-target]');
+ if (!btn) return;
+
+ btn.addEventListener('click', function () {
+ var text = getCodeText(wrapper);
+ if (!text) return;
+
+ navigator.clipboard.writeText(text).then(function () {
+ var copyIcon = btn.querySelector('[data-feather="copy"]');
+ var checkIcon = btn.querySelector('[data-feather="check"]');
+ var liveRegion = wrapper.querySelector('.code-copy-status');
+ if (copyIcon) copyIcon.style.display = 'none';
+ if (checkIcon) checkIcon.classList.remove('hidden');
+ btn.classList.add('is-copied');
+ if (liveRegion) liveRegion.textContent = 'Code copied to clipboard.';
+
+ setTimeout(function () {
+ if (copyIcon) copyIcon.style.display = '';
+ if (checkIcon) checkIcon.classList.add('hidden');
+ btn.classList.remove('is-copied');
+ if (liveRegion) liveRegion.textContent = '';
+ }, 2000);
+ }).catch(function () {
+ // silent fail for insecure contexts
+ });
+ });
+ }
+
+ document.addEventListener('DOMContentLoaded', function () {
+ document.querySelectorAll('.code-block-wrapper').forEach(initBlock);
+ });
+})();