Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 213 additions & 3 deletions docs/javascripts/model-switcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,223 @@
if (requested && available.includes(requested)) apply(requested);
};

let prevTabState = null;

const syncTabIndicator = () => {
const list = document.querySelector(".md-tabs__list");
if (!list) return;
const activeLink = list.querySelector(".md-tabs__item--active > .md-tabs__link, .md-tabs__link--active");
if (!activeLink) return;

const listRect = list.getBoundingClientRect();
const linkRect = activeLink.getBoundingClientRect();
const nextLeft = linkRect.left - listRect.left;
const nextWidth = linkRect.width;
const nextHeight = linkRect.height;

let indicator = list.querySelector(".gemma-tab-indicator");
if (!indicator) {
indicator = document.createElement("span");
indicator.className = "gemma-tab-indicator";
list.appendChild(indicator);
}
list.classList.add("has-sliding-indicator");
indicator.style.height = `${nextHeight}px`;

if (prevTabState && (Math.abs(prevTabState.left - nextLeft) > 1 || Math.abs(prevTabState.width - nextWidth) > 1)) {
indicator.style.transition = "none";
indicator.style.transform = `translate3d(${prevTabState.left}px, -50%, 0)`;
indicator.style.width = `${prevTabState.width}px`;
void indicator.offsetWidth;
requestAnimationFrame(() => {
indicator.style.transition = "";
indicator.style.transform = `translate3d(${nextLeft}px, -50%, 0)`;
indicator.style.width = `${nextWidth}px`;
});
} else {
indicator.style.transform = `translate3d(${nextLeft}px, -50%, 0)`;
indicator.style.width = `${nextWidth}px`;
}

prevTabState = { left: nextLeft, width: nextWidth };
};

document.addEventListener("click", (event) => {
if (event.target.closest?.(".md-search__button, [data-md-component=search]")) {
setTimeout(initSearchModalDefaults, 20);
setTimeout(initSearchModalDefaults, 150);
}
const tabLink = event.target.closest?.(".md-tabs__link");
if (tabLink) {
const list = tabLink.closest(".md-tabs__list");
const currentActive = list?.querySelector(".md-tabs__item--active > .md-tabs__link, .md-tabs__link--active");
if (list && currentActive) {
const listRect = list.getBoundingClientRect();
const linkRect = currentActive.getBoundingClientRect();
prevTabState = { left: linkRect.left - listRect.left, width: linkRect.width };
}
}
for (const picker of document.querySelectorAll(".page-context__picker")) {
if (!picker.contains(event.target)) closePicker(picker);
}
});

if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init);
else init();
if (window.document$?.subscribe) window.document$.subscribe(init);
window.addEventListener("keydown", (event) => {
if (((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") || event.key === "/") {
setTimeout(initSearchModalDefaults, 20);
setTimeout(initSearchModalDefaults, 150);
}
});
window.addEventListener("resize", syncTabIndicator);

const DEFAULT_SEARCH_TOPICS = [
{
title: "Quickstart with Gemma 4",
path: ["Get started", "Quickstart"],
url: "get-started/",
excerpt: "Choose a Gemma 4 model size (E2B to 31B) and run your first inference locally or in Google Colab."
},
{
title: "Gemma 4 model overview & benchmarks",
path: ["Models", "Gemma 4"],
url: "models/gemma-4/",
excerpt: "Architecture, context windows, multimodal inputs, and benchmark results across the Gemma 4 family."
},
{
title: "Run Gemma locally with Ollama",
path: ["Run", "Local"],
url: "deployment/ollama/",
excerpt: "Pull and run quantized Gemma 4 checkpoints on macOS, Linux, or Windows with a single command."
},
{
title: "Fine-tune Gemma with QLoRA",
path: ["Fine-tune", "Guides"],
url: "tuning/qlora/",
excerpt: "Memory-efficient 4-bit LoRA fine-tuning with Hugging Face TRL and PEFT on a single GPU."
},
{
title: "Multimodal vision, audio, and video",
path: ["Get started", "Multimodal"],
url: "get-started/capabilities/vision/",
excerpt: "Pass images, multi-frame video, and audio inputs to Gemma 4 and control token budgets."
},
{
title: "Function calling & tool use",
path: ["Get started", "Text"],
url: "get-started/capabilities/function-calling/",
excerpt: "Define structured tool schemas and parse model tool calls in multi-turn agent loops."
}
];

const resolveSiteBase = () => {
try {
const cfg = document.querySelector("#__config");
if (cfg) {
const parsed = JSON.parse(cfg.textContent);
const raw = new URL(parsed.base || ".", window.location.href).toString();
return raw.endsWith("/") ? raw : `${raw}/`;
}
} catch {
// Fallback to root
}
return new URL("/", window.location.href).toString();
};

const initSearchModalDefaults = () => {
let shadowRoot = null;
for (const div of document.querySelectorAll("body > div")) {
if (div.shadowRoot && div.shadowRoot.querySelector(".z")) {
shadowRoot = div.shadowRoot;
break;
}
}
if (!shadowRoot || shadowRoot.__gemmaDefaultsInit) return;
const resultsPane = shadowRoot.querySelector(".z");
const input = shadowRoot.querySelector(".f input");
if (!resultsPane || !input) return;
shadowRoot.__gemmaDefaultsInit = true;

const base = resolveSiteBase();
const wrapper = document.createElement("div");
wrapper.className = "gemma-search-defaults";
wrapper.innerHTML =
`<h3 class="B">Suggested topics</h3>` +
`<ol class="b">` +
DEFAULT_SEARCH_TOPICS.map((item, idx) => {
const href = new URL(item.url, base).toString();
const crumbs = item.path.map((p) => `<li>${p}</li>`).join("");
return (
`<li>` +
`<a class="i${idx === 0 ? " h" : ""}" href="${href}">` +
`<div class="C">` +
`<div class="D"><menu class="t">${crumbs}</menu></div>` +
`<h2 class="x">${item.title}</h2>` +
`<div class="u">${item.excerpt}</div>` +
`</div>` +
`</a>` +
`</li>`
);
}).join("") +
`</ol>`;

resultsPane.insertBefore(wrapper, resultsPane.firstChild);

let activeIdx = 0;
const links = [...wrapper.querySelectorAll("a.i")];
const closeModal = () => shadowRoot.querySelector(".k button")?.click();
const setHighlight = (next) => {
links.forEach((a, i) => a.classList.toggle("h", i === next));
activeIdx = next;
};
links.forEach((a, i) => {
a.addEventListener("pointerenter", () => setHighlight(i));
a.addEventListener("click", () => closeModal());
});

const syncVisibility = () => {
const hasQuery = input.value.trim().length > 0;
wrapper.style.display = hasQuery ? "none" : "";
};

input.addEventListener("input", syncVisibility);
input.addEventListener("focus", syncVisibility);
input.addEventListener("keydown", (e) => {
if (input.value.trim().length > 0) return;
if (e.key === "ArrowDown") {
e.preventDefault();
e.stopPropagation();
setHighlight((activeIdx + 1) % links.length);
links[activeIdx]?.scrollIntoView({ block: "nearest" });
} else if (e.key === "ArrowUp") {
e.preventDefault();
e.stopPropagation();
setHighlight((activeIdx - 1 + links.length) % links.length);
links[activeIdx]?.scrollIntoView({ block: "nearest" });
} else if (e.key === "Enter" && links[activeIdx]) {
e.preventDefault();
e.stopPropagation();
const dest = links[activeIdx].href;
closeModal();
window.location.href = dest;
}
});

const observer = new MutationObserver(syncVisibility);
observer.observe(resultsPane, { childList: true, subtree: true });
syncVisibility();
};

const onPageReady = () => {
init();
syncTabIndicator();
initSearchModalDefaults();
setTimeout(initSearchModalDefaults, 250);
if (typeof window.__initGemmaHome === "function") {
window.__initGemmaHome();
}
};

if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", onPageReady);
else onPageReady();
if (window.document$?.subscribe) window.document$.subscribe(onPageReady);
})();
51 changes: 17 additions & 34 deletions docs/stylesheets/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,76 +71,59 @@

/* -------------------------------------------------------------------
Admonitions
A tinted panel with a single accent rail on the left. Quieter than
Material's default full-colour title bar.
Clean editorial callouts: uniform 1px hairline, subtle surface tint,
crisp typographic title, no 3px left border rail or stock icons.
------------------------------------------------------------------- */
.md-typeset .admonition,
.md-typeset details {
margin: var(--gemma-space-5) 0;
padding: var(--gemma-space-3) var(--gemma-space-4);
font-size: 0.83rem;
padding: 0.95rem 1.2rem;
font-size: 0.84rem;
line-height: 1.6;
background-color: var(--gemma-surface-sunken);
border: 1px solid var(--gemma-border);
border-left: 3px solid var(--gemma-n300);
border-radius: var(--gemma-radius-md);
border-radius: var(--gemma-radius-lg);
box-shadow: none;
}

.md-typeset .admonition-title,
.md-typeset summary {
margin: 0 0 var(--gemma-space-1);
padding: 0 0 0 var(--gemma-space-5);
font-size: 0.83rem;
margin: 0 0 0.25rem;
padding: 0 !important;
font-size: 0.85rem;
font-weight: 600;
letter-spacing: -0.01em;
color: var(--md-default-fg-color);
background-color: transparent;
border: 0;
}

.md-typeset .admonition-title::before,
.md-typeset summary::before {
top: 0.1em;
left: 0;
width: 1rem;
height: 1rem;
display: none !important;
}

.md-typeset .admonition > :last-child,
.md-typeset details > :last-child {
margin-bottom: 0;
}

/* Material tints each type at `.md-typeset .admonition.note` (0,3,1), which outranks the
base rule above. Matching that specificity keeps every panel on the same neutral surface;
the type is carried by the rail colour instead. */
.md-typeset .admonition[class],
.md-typeset details[class] {
background-color: var(--gemma-surface-sunken);
}

/* Accent rail and icon tint per type. */
.md-typeset .admonition.note,
.md-typeset details.note {
border-left-color: var(--gemma-blue);
}

.md-typeset .note > .admonition-title::before,
.md-typeset .note > summary::before {
background-color: var(--gemma-blue);
}

.md-typeset .admonition.tip,
.md-typeset details.tip {
border-left-color: var(--gemma-status-success);
border-left: 1px solid var(--gemma-border);
}

.md-typeset .admonition.warning,
.md-typeset details.warning {
border-left-color: var(--gemma-status-warning);
background-color: color-mix(in srgb, var(--gemma-status-warning) 5%, var(--gemma-surface-sunken));
border-color: color-mix(in srgb, var(--gemma-status-warning) 28%, var(--gemma-border));
}

.md-typeset .admonition.danger,
.md-typeset details.danger {
border-left-color: var(--gemma-status-danger);
background-color: color-mix(in srgb, var(--gemma-status-danger) 5%, var(--gemma-surface-sunken));
border-color: color-mix(in srgb, var(--gemma-status-danger) 28%, var(--gemma-border));
}

/* -------------------------------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions docs/stylesheets/gemma.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@
--gemma-white: #FFFFFF;
--gemma-ink: #202124;

/* Landing-page gradient stops, derived from the palette. */
/* Landing-page stage & gradient stops, derived from the palette. */
--gemma-ink-raised: #152238;
--gemma-navy-900: #0e2042;
--gemma-navy-800: #102a5c;
--gemma-navy-700: #16366e;
--gemma-home-bg: #040d1a;
--gemma-home-bg-raised: #07152f;
--gemma-home-bg-mid: #040c1e;
--gemma-home-bg-deep: #020712;
--gemma-home-bg: #ffffff;
--gemma-home-bg-raised: #f2f5fb;
--gemma-home-bg-mid: #eef2f9;
--gemma-home-bg-deep: #141518;
--gemma-blue-strong: var(--gemma-blue-800);
--gemma-blue-hover: var(--gemma-blue-600);

Expand Down
Loading
Loading