diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js
new file mode 100644
index 0000000000..227762f59b
--- /dev/null
+++ b/assets/js/click-to-copy.js
@@ -0,0 +1,105 @@
+
+(function () {
+ 'use strict';
+
+ const COPY_ICON = ``;
+ const CHECK_ICON = ``;
+
+ function setText(btn, icon, label, copied) {
+ btn.innerHTML = icon + `${label}`;
+ btn.setAttribute('aria-label', label);
+ btn.classList.toggle('c2c-btn--copied', copied);
+ }
+
+ function writeToClipboard(text, btn) {
+ function onSuccess() {
+ setText(btn, CHECK_ICON, 'Copied!', true);
+ setTimeout(function () { setText(btn, COPY_ICON, 'Copy', false); }, 2000);
+ }
+
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(text).then(onSuccess).catch(onSuccess);
+ } else {
+ // Fallback
+ var ta = document.createElement('textarea');
+ ta.value = text;
+ ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;';
+ document.body.appendChild(ta);
+ ta.focus(); ta.select();
+ try { document.execCommand('copy'); } catch (_) {}
+ document.body.removeChild(ta);
+ onSuccess();
+ }
+ }
+
+ function addCopyButtons() {
+ var barePres = document.querySelectorAll('.td-content > pre:not(.mermaid)');
+ barePres.forEach(function (pre) {
+ if (pre.parentNode && pre.parentNode.classList.contains('highlight')) return;
+ var wrapper = document.createElement('div');
+ wrapper.className = 'highlight';
+ pre.parentNode.insertBefore(wrapper, pre);
+ wrapper.appendChild(pre);
+ });
+
+ var highlights = document.querySelectorAll('.td-content .highlight');
+ highlights.forEach(function (hl) {
+ if (hl.querySelector('.c2c-header')) return;
+
+ var codeEl = hl.querySelector('pre code') || hl.querySelector('pre');
+ var lang = '';
+ if (codeEl) {
+ var dataLang = codeEl.getAttribute('data-lang');
+ if (!dataLang && codeEl.className) {
+ var match = codeEl.className.match(/language-(\S+)/);
+ if (match) dataLang = match[1];
+ }
+ if (dataLang) {
+ lang = dataLang.trim().toUpperCase();
+ }
+ }
+
+ if (lang === 'SHELL' || lang === 'BASH' || lang === 'SH') {
+ lang = 'Terminal';
+ } else if (lang === 'YAML' || lang === 'YML') {
+ lang = 'YAML';
+ } else if (lang === 'JSON') {
+ lang = 'JSON';
+ } else if (lang === 'PYTHON' || lang === 'PY') {
+ lang = 'Python';
+ } else if (!lang) {
+ lang = 'Code';
+ }
+
+ var header = document.createElement('div');
+ header.className = 'c2c-header';
+
+ var langBadge = document.createElement('span');
+ langBadge.className = 'c2c-lang-badge';
+ langBadge.textContent = lang;
+ header.appendChild(langBadge);
+
+ // Create copy button
+ var btn = document.createElement('button');
+ btn.className = 'c2c-btn';
+ btn.setAttribute('type', 'button');
+ setText(btn, COPY_ICON, 'Copy', false);
+
+ btn.addEventListener('click', function () {
+ var text = (codeEl.textContent || codeEl.innerText || '').trim();
+ writeToClipboard(text, btn);
+ });
+
+ header.appendChild(btn);
+
+ // Prepend header to highlight block
+ hl.insertBefore(header, hl.firstChild);
+ });
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', addCopyButtons);
+ } else {
+ document.addCopyButtons || addCopyButtons();
+ }
+})();
diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss
index 919482972e..76f6c9bbf9 100644
--- a/assets/scss/_styles_project.scss
+++ b/assets/scss/_styles_project.scss
@@ -536,7 +536,7 @@ nav.foldable-nav {
color: $home-card-body-color;
background-color: $home-card-body-bg;
- // make links in the card have a dotted underline
+
a {
color: lighten($primary, 15%);
border-bottom: 0.1em dotted lighten($primary, 15%);
@@ -608,7 +608,7 @@ nav.foldable-nav {
}
#pageContent {
- // make card body a flex coloumn so github badge can sit at the bottom
+
.card-deck .card-body,
.card-columns .card-body {
display: flex;
@@ -839,17 +839,13 @@ nav.foldable-nav {
}
}
-// --------------------------------------------------
-// Installing Kubeflow page
-// --------------------------------------------------
+
.distributions-table thead {
background-color: $navbar-bg;
color: $white;
}
-// --------------------------------------------------
-// 404 page
-// --------------------------------------------------
+
.error-page {
margin-top: 120px;
@@ -882,10 +878,6 @@ nav.foldable-nav {
}
}
-// --------------------------------------------------
-// for tabbed code blocks
-// https://github.com/kubeflow/website/pull/2779
-// --------------------------------------------------
.nav-tabs {
border-bottom: none !important;
}
@@ -910,9 +902,7 @@ nav.foldable-nav {
}
}
-// --------------------------------------------------
-// for feedback partial
-// --------------------------------------------------
+
.feedback--answer {
display: inline-block;
}
@@ -930,31 +920,236 @@ nav.foldable-nav {
display: block;
}
-// --------------------------------------------------
-// code styling
-// --------------------------------------------------
+
.td-content {
- // set the background color of inline code
p code,
li > code,
table code {
background-color: $code-inline-bg;
}
- // set the background color of code blocks
.highlight pre {
background-color: $code-block-bg;
}
- // ensure links with code in them have the correct color
a > code {
color: unset;
}
}
-// --------------------------------------------------
-// alert styling
-// --------------------------------------------------
+
.alert {
background-color: $alert-bg;
}
+
+
+
+.td-content .highlight {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ overflow: visible !important;
+
+ pre {
+ margin: 0 !important;
+ border-top-left-radius: 0 !important;
+ border-top-right-radius: 0 !important;
+ padding-right: 1.5rem !important;
+ }
+}
+
+// ---- Header container ----
+.c2c-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.35rem 0.75rem;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+ font-size: 0.7rem;
+ font-weight: 600;
+ letter-spacing: 0.05em;
+ // Inherit the card's top-border-radius from .highlight (no explicit radius needed)
+ border-radius: inherit;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+// Theme specific colors for header
+@at-root [data-theme="light"] .c2c-header {
+ background-color: #f3f4f6;
+ border-bottom: 1px solid #e5e7eb;
+ color: #4b5563;
+}
+
+@at-root [data-theme="dark"] .c2c-header {
+ background-color: rgba(0, 0, 0, 0.25);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+ color: #9ca3af;
+}
+
+.c2c-lang-badge {
+ text-transform: uppercase;
+ font-size: 0.65rem;
+ opacity: 0.85;
+}
+
+// ---- The button itself ----
+.c2c-btn {
+ position: relative;
+ z-index: 10;
+
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.6rem;
+ height: 1.6rem;
+ padding: 0;
+ border-radius: 0.3rem;
+
+ // Reset browser default