Repository: loft-sh/vcluster-docs
Pull request #707: feat: reusable glossary of terms
Author: login: Piotr1215
Created: 2025-05-21T12:05:32Z | Merged: 2025-05-22T08:04:30Z

<!-- 
When changing something in a file, our linting system `vale`, will treat the whole file as changed and will lint it. 
In this case, follow the instructions from vale and fix the linting issues. 
If there are too many errors, ask the tech writer in PR comment to fix the issues.
Read more about working with vale in the contribution guidelines: https://github.com/loft-sh/vcluster-docs/blob/main/CONTRIBUTING.md#style-guide-automation-style-guide-automation
-->
# Content Description
<!-- Brief overview of changes (1-2 sentences) -->

This PR introduces a new `GlossaryTerm` component that enables hover definitions and optional reference links for technical terms throughout the documentation.

## Overview

![c4_style_glossary_system](https://github.com/user-attachments/assets/28692e96-44e6-4cde-9e49-7decfe9ce7be)


The glossary system consists of:
1. A YAML data file containing terminology definitions
2. A React component that renders terms with hover tooltips
3. Styling that differentiates between regular terms and terms with reference links

## Features

- **First-occurrence highlighting:** Only the first occurrence of each term on a page is styled with the glossary tooltip
- **Hover definitions:** Terms display detailed tooltips when users hover over them
- **Reference links:** Optional links to related documentation pages
- **Consistent visual styling:** Leverages vCluster brand colors 
- **Mobile-friendly:** Responsive tooltip design adapts to smaller screens

## How It Works

![image](https://github.com/user-attachments/assets/839b678d-f0b3-4dc7-92a0-c1705c4c4978)

![image](https://github.com/user-attachments/assets/2c3004a3-4538-4e9c-bc57-8a15b3bd9a8e)

### Data Structure

The glossary terms are defined in `src/data/glossary.yaml` using this structure:

```yaml
term-id:
  term: "Display Name"
  definition: "The definition text that appears in the tooltip."
  related: ["other-term-id", "another-term-id"]
  type: "vcluster" # or "platform"
  url: "/docs/path/to/reference/page" # Optional
```

### Hover highlight

The component works in two modes:

1. **Basic glossary term:**
   - Displays with bold text and light gray dashed underline
   - Shows tooltip with definition on hover

2. **Term with reference link:**
   - Displays like a regular glossary term in the text
   - Tooltip header appears as a blue link with underline
   - Special "↗" indicator shows that clicking will navigate to documentation

### Development

A new CI has been added that checks for orphaned references and ensures that a glossary term can be added to a give page only once. The same can be run as an npm script with `npm run validate-glossary`.  

## Preview Link 
<!-- The preview link from Netlify needs `/docs` appended after it.
If you want the preview link to be available in the Linear issue, you must include the word `preview` in the markdown link name [Document Preview](https://netlify.preview/docs/xxxx). -->
https://deploy-preview-707--vcluster-docs-site.netlify.app/docs/vcluster/next/introduction/what-are-virtual-clusters
https://deploy-preview-707--vcluster-docs-site.netlify.app/docs/vcluster/next/reference/glossary
https://deploy-preview-707--vcluster-docs-site.netlify.app/docs/platform/next/reference/glossary

## Internal Reference
<!--Add the GitHub or Linear ticket reference-->
Closes DOC-645
Closes DOC-644




Diff:
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 000000000..deb23d0b4
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,28 @@
+# GitHub Workflows
+
+This directory contains GitHub Actions workflows for the vCluster documentation.
+
+## Validate Glossary Terms
+
+The `validate-glossary.yml` workflow checks that all terms referenced in `<GlossaryTerm>` components throughout the documentation exist in the glossary data file.
+
+### When it runs:
+- On all PRs to the main branch that change MDX files or the glossary data
+- When manually triggered via the GitHub Actions UI
+
+### What it checks:
+- Scans all MDX files in the docs, vcluster, and platform directories
+- Looks for `<GlossaryTerm term="...">` usage
+- Validates that each referenced term exists in `src/data/glossary.yaml`
+- Fails if any referenced terms are missing
+
+### How to fix failures:
+If this workflow fails, you have two options:
+1. Add the missing term to the glossary data file
+2. Fix the typo in the term reference in your MDX file
+
+### Running locally:
+You can run the same check locally with:
+```bash
+npm run validate-glossary
+```
\ No newline at end of file
diff --git a/.github/workflows/validate-glossary.yml b/.github/workflows/validate-glossary.yml
new file mode 100644
index 000000000..8b5afb2cd
--- /dev/null
+++ b/.github/workflows/validate-glossary.yml
@@ -0,0 +1,33 @@
+name: Validate Glossary Terms
+
+on:
+  pull_request:
+    branches: [ main ]
+    paths:
+      - '**/*.mdx'
+      - 'src/data/glossary.yaml'
+      - 'src/components/GlossaryTerm/**'
+      - 'scripts/validate-glossary-terms.js'
+
+  # Allow manual triggering
+  workflow_dispatch:
+
+jobs:
+  validate-glossary:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v3
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v3
+        with:
+          node-version: '20'
+          cache: 'npm'
+
+      - name: Install dependencies
+        run: npm install
+
+      - name: Validate glossary terms
+        run: npm run validate-glossary
\ No newline at end of file
diff --git a/docusaurus.config.js b/docusaurus.config.js
index bc3c5cbbd..d756abbc6 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -99,6 +99,23 @@ const config = {
   plugins: [
     "docusaurus-plugin-sass",
     "plugin-image-zoom",
+    function(context, options) {
+      return {
+        name: 'yaml-loader',
+        configureWebpack() {
+          return {
+            module: {
+              rules: [
+                {
+                  test: /\.ya?ml$/,
+                  use: 'yaml-loader',
+                },
+              ],
+            },
+          };
+        },
+      };
+    },
 
     [
       "@docusaurus/plugin-content-docs",
diff --git a/package-lock.json b/package-lock.json
index 25397eed1..4826808a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,7 +16,7 @@
         "@saucelabs/theme-github-codeblock": "^0.3.0",
         "clsx": "^2.1.1",
         "docusaurus-plugin-sass": "^0.2.6",
-        "mermaid": "^10.6.1",
+        "mermaid": "^10.9.3",
         "plugin-image-zoom": "flexanalytics/plugin-image-zoom",
         "prism-react-renderer": "^2.4.1",
         "raw-loader": "^4.0.2",
@@ -24,7 +24,8 @@
         "react-dom": "^18.0.0",
         "redocusaurus": "^2.2.3",
         "resolve-glob": "^1.0.0",
-        "sass": "^1.87.0"
+        "sass": "^1.87.0",
+        "yaml-loader": "^0.8.0"
       },
       "devDependencies": {
         "@docusaurus/module-type-aliases": "3.7.0",
@@ -11034,6 +11035,12 @@
       "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
       "license": "MIT"
     },
+    "node_modules/javascript-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
+      "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==",
+      "license": "MIT"
+    },
     "node_modules/jest-util": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
@@ -20890,6 +20897,32 @@
       "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==",
       "license": "Apache-2.0"
     },
+    "node_modules/yaml-loader": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.8.1.tgz",
+      "integrity": "sha512-BCEndnUoi3BaZmePkwGGe93txRxLgMhBa/gE725v1/GHnura8QvNs7c4+4C1yyhhKoj3Dg63M7IqhA++15j6ww==",
+      "license": "MIT",
+      "dependencies": {
+        "javascript-stringify": "^2.0.1",
+        "loader-utils": "^2.0.0",
+        "yaml": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/yaml-loader/node_modules/yaml": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
+      "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+      "license": "ISC",
+      "bin": {
+        "yaml": "bin.mjs"
+      },
+      "engines": {
+        "node": ">= 14.6"
+      }
+    },
     "node_modules/yargs": {
       "version": "17.7.2",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
diff --git a/package.json b/package.json
index 4166a6314..bde6bc1e9 100644
--- a/package.json
+++ b/package.json
@@ -5,13 +5,15 @@
   "scripts": {
     "docusaurus": "docusaurus",
     "start": "docusaurus start",
+    "prebuild": "npm run validate-glossary",
     "build": "docusaurus build",
     "swizzle": "docusaurus swizzle",
     "deploy": "docusaurus deploy",
     "clear": "docusaurus clear",
     "serve": "docusaurus serve",
     "write-translations": "docusaurus write-translations",
-    "write-heading-ids": "docusaurus write-heading-ids"
+    "write-heading-ids": "docusaurus write-heading-ids",
+    "validate-glossary": "node scripts/validate-glossary-terms.js"
   },
   "dependencies": {
     "@docusaurus/core": "3.7.0",
@@ -30,11 +32,14 @@
     "react-dom": "^18.0.0",
     "redocusaurus": "^2.2.3",
     "resolve-glob": "^1.0.0",
-    "sass": "^1.87.0"
+    "sass": "^1.87.0",
+    "yaml-loader": "^0.8.0"
   },
   "devDependencies": {
     "@docusaurus/module-type-aliases": "3.7.0",
-    "@docusaurus/types": "3.7.0"
+    "@docusaurus/types": "3.7.0",
+    "glob": "^10.3.10",
+    "js-yaml": "^4.1.0"
   },
   "browserslist": {
     "production": [
diff --git a/platform/reference/glossary.mdx b/platform/reference/glossary.mdx
new file mode 100644
index 000000000..bf43dab3e
--- /dev/null
+++ b/platform/reference/glossary.mdx
@@ -0,0 +1,36 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in the platform documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+<!-- vale off -->
+# Platform glossary {#platform-glossary}
+<!-- vale on -->
+
+This page contains definitions for common terms used throughout the platform documentation.
+
+<!-- vale off -->
+{Object.entries(glossaryData)
+  .filter(([key, entry]) => entry.type === 'platform')
+  .sort((a, b) => a[1].term.localeCompare(b[1].term))
+  .map(([key, entry]) => (
+    <div key={key} id={key} style={{ marginBottom: '2.5rem', borderBottom: '1px solid #eee', paddingBottom: '1.5rem' }}>
+      <h3>{entry.term}</h3>
+      <p>{entry.definition}</p>
+      
+      {entry.related && (
+        <p><strong>Related terms:</strong> {entry.related.map((term, index) => (
+          <span key={term}>
+            {index > 0 && ', '}
+            <span>{glossaryData[term]?.term || term}</span>
+          </span>
+        ))}</p>
+      )}
+    </div>
+  ))
+}
+<!-- vale on -->
\ No newline at end of file
diff --git a/scripts/generate-glossary-usage.js b/scripts/generate-glossary-usage.js
new file mode 100755
index 000000000..ea6f4279f
--- /dev/null
+++ b/scripts/generate-glossary-usage.js
@@ -0,0 +1,106 @@
+#!/usr/bin/env node
+
+/**
+ * generate-glossary-usage.js
+ * 
+ * This script scans all MDX files to find where each glossary term is used
+ * and generates a JSON file with the results, organizing by term type.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const yaml = require('js-yaml');
+const glob = require('glob');
+
+// Load the glossary data
+const glossaryPath = path.join(__dirname, '../src/data/glossary.yaml');
+const glossaryData = yaml.load(fs.readFileSync(glossaryPath, 'utf8'));
+
+// Define the regex pattern to match GlossaryTerm components
+const glossaryTermPattern = /<GlossaryTerm\s+term=["']([^"']+)["']/g;
+
+// Get all MDX files
+const mdxFiles = [
+  ...glob.sync('docs/**/*.mdx'),
+  ...glob.sync('vcluster/**/*.mdx'),
+  ...glob.sync('platform/**/*.mdx'),
+];
+
+// Initialize usage data structure
+const usageData = {};
+
+// Initialize each term with an empty array of usages and include type information
+Object.keys(glossaryData).forEach(term => {
+  usageData[term] = {
+    type: glossaryData[term].type || 'general',
+    term: glossaryData[term].term,
+    usages: []
+  };
+});
+
+// Scan each MDX file for GlossaryTerm components
+mdxFiles.forEach(filePath => {
+  const content = fs.readFileSync(filePath, 'utf8');
+  const filename = path.basename(filePath);
+  
+  // Extract title from frontmatter
+  const titleMatch = content.match(/---\s*\r?\n([\s\S]*?)\r?\n---/);
+  let title = filename;
+  
+  if (titleMatch && titleMatch[1]) {
+    const titleLineMatch = titleMatch[1].match(/title:\s*["']?(.*?)["']?\s*(\r?\n|$)/);
+    if (titleLineMatch && titleLineMatch[1]) {
+      title = titleLineMatch[1];
+    }
+  }
+  
+  // Determine document section based on path
+  let section = 'general';
+  if (filePath.startsWith('vcluster/')) {
+    section = 'vcluster';
+  } else if (filePath.startsWith('platform/')) {
+    section = 'platform';
+  }
+  
+  // Collect all term usage
+  let match;
+  while ((match = glossaryTermPattern.exec(content)) !== null) {
+    const term = match[1];
+    
+    if (glossaryData[term]) {
+      if (!usageData[term].usages.find(u => u.path === filePath)) {
+        usageData[term].usages.push({
+          path: filePath,
+          title: title,
+          displayPath: filePath.replace(/\.mdx$/, ''),
+          section: section
+        });
+      }
+    }
+  }
+});
+
+// Write the usage data to a JSON file
+const outputPath = path.join(__dirname, '../src/data/glossary-usage.json');
+fs.writeFileSync(outputPath, JSON.stringify(usageData, null, 2));
+
+// Generate summary statistics
+const termsByType = {
+  vcluster: {
+    total: Object.keys(glossaryData).filter(k => glossaryData[k].type === 'vcluster').length,
+    used: Object.keys(usageData).filter(k => 
+      glossaryData[k].type === 'vcluster' && usageData[k].usages.length > 0
+    ).length
+  },
+  platform: {
+    total: Object.keys(glossaryData).filter(k => glossaryData[k].type === 'platform').length,
+    used: Object.keys(usageData).filter(k => 
+      glossaryData[k].type === 'platform' && usageData[k].usages.length > 0
+    ).length
+  }
+};
+
+console.log(`Glossary usage data generated at ${outputPath}`);
+console.log(`Found usages for ${Object.keys(usageData).filter(k => usageData[k].usages.length > 0).length} terms in ${mdxFiles.length} MDX files`);
+console.log(`vCluster terms: ${termsByType.vcluster.used}/${termsByType.vcluster.total} used`);
+console.log(`Platform terms: ${termsByType.platform.used}/${termsByType.platform.total} used`);
\ No newline at end of file
diff --git a/scripts/validate-glossary-terms.js b/scripts/validate-glossary-terms.js
new file mode 100755
index 000000000..7727d3af3
--- /dev/null
+++ b/scripts/validate-glossary-terms.js
@@ -0,0 +1,83 @@
+#!/usr/bin/env node
+
+/**
+ * validate-glossary-terms.js
+ * 
+ * This script scans MDX files for <GlossaryTerm> tags and validates that:
+ * 1. The referenced terms exist in the glossary.yaml file
+ * 2. Each term is only wrapped once per document (first occurrence only)
+ * 
+ * Usage:
+ *   npm run validate-glossary
+ */
+
+const fs = require('fs');
+const path = require('path');
+const yaml = require('js-yaml');
+const glob = require('glob');
+
+// Load the glossary data
+const glossaryPath = path.join(__dirname, '../src/data/glossary.yaml');
+const glossaryData = yaml.load(fs.readFileSync(glossaryPath, 'utf8'));
+
+// Define the regex pattern to match GlossaryTerm components
+const glossaryTermPattern = /<GlossaryTerm\s+term=["']([^"']+)["']/g;
+
+// Get all MDX files
+// Handle both glob v7 and v10 formats
+let mdxFiles = [];
+try {
+  // For glob v7
+  mdxFiles = glob.sync('**/*.mdx', {
+    ignore: [
+      'node_modules/**',
+      'build/**',
+      '.docusaurus/**',
+      '.vscode/**'
+    ]
+  });
+} catch (error) {
+  // For newer versions, fall back to the directory-specific approach
+  mdxFiles = [
+    ...glob.sync('docs/**/*.mdx'),
+    ...glob.sync('vcluster/**/*.mdx'),
+    ...glob.sync('platform/**/*.mdx'),
+    ...glob.sync('platform_versioned_docs/**/*.mdx'),
+    ...glob.sync('vcluster_versioned_docs/**/*.mdx'),
+  ];
+}
+
+let hasErrors = false;
+
+// Scan each MDX file for GlossaryTerm components
+mdxFiles.forEach(filePath => {
+  const content = fs.readFileSync(filePath, 'utf8');
+  let match;
+  const usedTermsInFile = new Set();
+  
+  while ((match = glossaryTermPattern.exec(content)) !== null) {
+    const term = match[1];
+    
+    // Check if term exists in glossary
+    if (!glossaryData[term]) {
+      console.error(`Error: Term "${term}" not found in glossary, but referenced in ${filePath}`);
+      hasErrors = true;
+    }
+    
+    // Check if term has already been used in this file
+    if (usedTermsInFile.has(term)) {
+      console.error(`Error: Term "${term}" is wrapped multiple times in ${filePath}. Only wrap the first occurrence.`);
+      hasErrors = true;
+    }
+    
+    usedTermsInFile.add(term);
+  }
+});
+
+if (hasErrors) {
+  console.error('\nValidation failed. Please fix the errors above before continuing.');
+  process.exit(1);
+} else {
+  console.log('\nValidation successful! All glossary terms referenced in MDX files exist in the glossary.');
+  process.exit(0);
+}
\ No newline at end of file
diff --git a/src/components/GlossaryTerm/index.jsx b/src/components/GlossaryTerm/index.jsx
new file mode 100644
index 000000000..69f813e6c
--- /dev/null
+++ b/src/components/GlossaryTerm/index.jsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import styles from './styles.module.css';
+import glossaryData from '@site/src/data/glossary.yaml';
+
+const GlossaryTerm = ({ term, children }) => {
+  
+  // Get term data
+  const termData = glossaryData[term];
+  
+  // Handle missing term data
+  if (!termData) {
+    return <>{children || term}</>;
+  }
+  
+  // For first occurrence, render with tooltip
+  const termContent = children || termData.term;
+  const hasUrl = termData.url && termData.url.length > 0;
+  
+  return (
+    <span className={styles.termWrapper}>
+      {hasUrl ? (
+        <a href={termData.url} className={styles.term}>
+          {termContent}
+        </a>
+      ) : (
+        <span className={styles.term}>
+          {termContent}
+        </span>
+      )}
+      <span className={styles.tooltip}>
+        <span className={styles.tooltipHeader}>
+          {hasUrl ? (
+            <a href={termData.url} className={styles.tooltipHeaderLink}>
+              {termData.term}
+            </a>
+          ) : (
+            termData.term
+          )}
+        </span>
+        <span className={styles.tooltipDefinition}>{termData.definition}</span>
+        {termData.related && (
+          <span className={styles.related}>
+            <span>Related: </span>
+            {termData.related.map((relatedTermId, index) => {
+              const relatedTerm = glossaryData[relatedTermId];
+              return (
+                <span key={relatedTermId}>
+                  {index > 0 && ', '}
+                  {relatedTerm ? (
+                    <span className={styles.relatedTerm}>
+                      {relatedTerm.term}
+                    </span>
+                  ) : (
+                    relatedTermId
+                  )}
+                </span>
+              );
+            })}
+          </span>
+        )}
+      </span>
+    </span>
+  );
+};
+
+export default GlossaryTerm;
\ No newline at end of file
diff --git a/src/components/GlossaryTerm/styles.module.css b/src/components/GlossaryTerm/styles.module.css
new file mode 100644
index 000000000..8892021fc
--- /dev/null
+++ b/src/components/GlossaryTerm/styles.module.css
@@ -0,0 +1,132 @@
+.termWrapper {
+  display: inline-block;
+  position: relative;
+}
+
+.term {
+  cursor: help;
+  font-weight: normal;
+  color: inherit;
+  text-decoration: underline;
+  text-decoration-style: dashed;
+  text-decoration-color: #444444;
+  text-decoration-thickness: 1.5px;
+  text-underline-offset: 2px;
+}
+
+.term:hover {
+  color: #ED7200;
+}
+
+/* Linked terms in the text - same styling as normal terms */
+a.term {
+  cursor: help;
+  text-decoration: underline;
+  text-decoration-style: dashed;
+  text-decoration-color: #444444;
+  text-decoration-thickness: 1.5px;
+  text-underline-offset: 2px;
+}
+
+.tooltip {
+  visibility: hidden;
+  opacity: 0;
+  position: absolute;
+  z-index: 9999; /* Higher z-index to ensure it appears on top */
+  width: 280px;
+  background-color: white;
+  border: 1px solid #ddd;
+  border-radius: 6px;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
+  padding: 12px;
+  top: 100%;
+  left: 0;
+  margin-top: 8px;
+  user-select: text;
+  transition: visibility 0s, opacity 0.2s ease;
+  pointer-events: none; /* Allow clicking through when hidden */
+  display: block; /* Force block display for tooltip content */
+}
+
+/* Simple tooltip arrow */
+.tooltip:before {
+  content: '';
+  position: absolute;
+  top: -8px;
+  left: 15px;
+  border-left: 8px solid transparent;
+  border-right: 8px solid transparent;
+  border-bottom: 8px solid white;
+  z-index: 1;
+}
+
+.tooltip:after {
+  content: '';
+  position: absolute;
+  top: -9px;
+  left: 14px;
+  border-left: 9px solid transparent;
+  border-right: 9px solid transparent;
+  border-bottom: 9px solid #ddd;
+  z-index: 0;
+}
+
+/* Only show tooltip on hover over the term wrapper */
+.termWrapper:hover .tooltip {
+  visibility: visible;
+  opacity: 1;
+  pointer-events: auto; /* Re-enable pointer events when visible */
+}
+
+/* Regular tooltip header */
+.tooltipHeader {
+  color: #ED7200;
+  font-weight: bold;
+  font-size: 16px;
+  margin-bottom: 8px;
+  display: block; /* Make it display as a block element */
+}
+
+/* Linked tooltip header - standard web link style */
+.tooltipHeaderLink {
+  color: #077EEC;
+  text-decoration: underline;
+  cursor: pointer;
+  font-weight: bold;
+  font-size: 16px;
+  margin-bottom: 8px;
+}
+
+.tooltipHeaderLink:hover {
+  text-decoration: underline;
+  color: #00bdff;
+}
+
+/* Add link icon */
+.tooltipHeaderLink:after {
+  content: " ↗";
+  font-size: 0.8em;
+  display: inline-block;
+  margin-left: 3px;
+}
+
+.tooltipDefinition {
+  font-size: 14px;
+  line-height: 1.5;
+  margin-bottom: 10px;
+  display: block; /* Make it display as a block element */
+}
+
+.related {
+  margin-top: 10px;
+  font-size: 12px;
+  color: #666;
+  border-top: 1px solid #eee;
+  padding-top: 8px;
+  display: block; /* Make it display as a block element */
+}
+
+.relatedTerm {
+  color: #333;
+  font-weight: 500;
+}
\ No newline at end of file
diff --git a/src/css/general.scss b/src/css/general.scss
index 06815a6ef..241544596 100644
--- a/src/css/general.scss
+++ b/src/css/general.scss
@@ -84,5 +84,4 @@ table th:first-child:empty {
 /* Button: Edit this page */
 .container > .row > .col > div > .margin-vert--xl a:hover {
   background: var(--ifm-color-primary);
-}
-
+}
\ No newline at end of file
diff --git a/src/data/glossary-usage.json b/src/data/glossary-usage.json
new file mode 100644
index 000000000..72246f4f2
--- /dev/null
+++ b/src/data/glossary-usage.json
@@ -0,0 +1,136 @@
+{
+  "vcluster": {
+    "type": "vcluster",
+    "term": "vCluster",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "virtual-cluster": {
+    "type": "vcluster",
+    "term": "virtual cluster",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "host-cluster": {
+    "type": "vcluster",
+    "term": "host cluster",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "syncer": {
+    "type": "vcluster",
+    "term": "syncer",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "multi-tenancy": {
+    "type": "vcluster",
+    "term": "multi-tenancy",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "k3s": {
+    "type": "vcluster",
+    "term": "K3s",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "k8s": {
+    "type": "vcluster",
+    "term": "K8s",
+    "usages": []
+  },
+  "k0s": {
+    "type": "vcluster",
+    "term": "K0s",
+    "usages": [
+      {
+        "path": "vcluster/_partials/what-are-virtual-clusters.mdx",
+        "title": "what-are-virtual-clusters.mdx",
+        "displayPath": "vcluster/_partials/what-are-virtual-clusters",
+        "section": "vcluster"
+      }
+    ]
+  },
+  "platform": {
+    "type": "platform",
+    "term": "the platform",
+    "usages": []
+  },
+  "project": {
+    "type": "platform",
+    "term": "project",
+    "usages": []
+  },
+  "team": {
+    "type": "platform",
+    "term": "team",
+    "usages": []
+  },
+  "user": {
+    "type": "platform",
+    "term": "user",
+    "usages": []
+  },
+  "template": {
+    "type": "platform",
+    "term": "template",
+    "usages": []
+  },
+  "virtual-cluster-template": {
+    "type": "platform",
+    "term": "virtual cluster template",
+    "usages": []
+  },
+  "namespace": {
+    "type": "platform",
+    "term": "namespace",
+    "usages": []
+  },
+  "access-key": {
+    "type": "platform",
+    "term": "access key",
+    "usages": []
+  },
+  "sleep-mode": {
+    "type": "platform",
+    "term": "sleep mode",
+    "usages": []
+  }
+}
\ No newline at end of file
diff --git a/src/data/glossary.yaml b/src/data/glossary.yaml
new file mode 100644
index 000000000..e16734f42
--- /dev/null
+++ b/src/data/glossary.yaml
@@ -0,0 +1,105 @@
+vcluster:
+  term: "vCluster"
+  definition: "An open-source software product that creates and manages virtual Kubernetes clusters inside a host Kubernetes cluster. vCluster improves isolation and multi-tenancy capabilities while reducing infrastructure costs."
+  related: ["virtual-cluster", "host-cluster"]
+  type: "vcluster"
+
+virtual-cluster:
+  term: "Virtual Cluster"
+  definition: "A certified Kubernetes distribution that runs as an isolated, virtual environment nested inside a physical host cluster. Virtual clusters run inside host cluster namespaces but operate as independent Kubernetes environments, each with its own API server, control plane, syncer, and set of resources."
+  related: ["vcluster", "host-cluster"]
+  type: "vcluster"
+
+host-cluster:
+  term: "Host Cluster"
+  definition: "The physical Kubernetes cluster where virtual clusters are deployed and run. The host cluster provides the infrastructure resources (CPU, memory, storage, networking) that virtual clusters leverage, while maintaining isolation between different virtual environments."
+  related: ["virtual-cluster"]
+  type: "vcluster"
+
+syncer:
+  term: "Syncer"
+  definition: "A component in vCluster that synchronizes resources between the virtual cluster and the host cluster, enabling virtual clusters to function while maintaining isolation."
+  related: ["vcluster", "virtual-cluster"]
+  type: "vcluster"
+  url: "/docs/vcluster/configure/vcluster-yaml/sync/"
+
+multi-tenancy:
+  term: "Multi-tenancy"
+  definition: "The capability to host multiple separate users, teams, or workloads on the same infrastructure while providing isolation between them. Virtual clusters enhance multi-tenancy in Kubernetes environments."
+  related: ["virtual-cluster"]
+  type: "vcluster"
+
+k3s:
+  term: "K3s"
+  definition: "A lightweight, certified Kubernetes distribution often used as the default distribution for virtual clusters due to its minimal resource requirements and fast startup time."
+  related: ["k8s", "k0s"]
+  type: "vcluster"
+  url: "https://k3s.io/"
+
+k8s:
+  term: "K8s"
+  definition: "The standard Kubernetes distribution that can be used in virtual clusters, offering full compatibility with upstream Kubernetes features."
+  related: ["k3s", "k0s"]
+  type: "vcluster"
+  url: "https://kubernetes.io/"
+
+k0s:
+  term: "K0s"
+  definition: "A lightweight, certified Kubernetes distribution that can be used in virtual clusters as an alternative to K3s or standard Kubernetes."
+  related: ["k3s", "k8s"]
+  type: "vcluster"
+  url: "https://k0sproject.io/"
+
+platform:
+  term: "The Platform"
+  definition: "The vCluster Platform that provides management, access control, and operational features for virtual clusters across multiple physical host clusters."
+  related: ["project"]
+  type: "platform"
+
+project:
+  term: "Project"
+  definition: "A logical grouping of resources in the platform that provides team isolation and resource quotas. Projects help organize virtual clusters and namespace resources."
+  related: ["platform", "team"]
+  type: "platform"
+
+team:
+  term: "Team"
+  definition: "A group of users in the platform with shared access to specific projects and resources."
+  related: ["project", "user"]
+  type: "platform"
+
+user:
+  term: "User"
+  definition: "An individual account in the platform with specific access permissions to projects and resources."
+  related: ["team"]
+  type: "platform"
+
+template:
+  term: "Template"
+  definition: "A reusable configuration pattern in the platform that defines the settings and resources for creating new virtual clusters or namespaces."
+  related: ["platform", "virtual-cluster-template"]
+  type: "platform"
+
+virtual-cluster-template:
+  term: "Virtual Cluster Template"
+  definition: "A predefined configuration in the platform for creating virtual clusters with consistent settings, policies, and resources."
+  related: ["template"]
+  type: "platform"
+
+namespace:
+  term: "Namespace"
+  definition: "A Kubernetes namespace that can be managed by the platform, providing isolation for resources within a cluster."
+  related: ["project"]
+  type: "platform"
+
+access-key:
+  term: "Access Key"
+  definition: "A secure credential in the platform that allows programmatic access to platform resources and API endpoints."
+  related: ["platform", "user"]
+  type: "platform"
+
+sleep-mode:
+  term: "Sleep Mode"
+  definition: "A platform feature that allows virtual clusters or namespaces to be paused when inactive, conserving resources and reducing costs."
+  related: ["platform"]
+  type: "platform"
diff --git a/src/theme/MDXComponents.js b/src/theme/MDXComponents.js
index 077bba9ca..e9213afcd 100644
--- a/src/theme/MDXComponents.js
+++ b/src/theme/MDXComponents.js
@@ -1,11 +1,9 @@
-  import React from 'react';
-  // Importing the original mapper + our components according to the Docusaurus doc
-  import MDXComponents from '@theme-original/MDXComponents';
-  import Columns from '@site/src/components/Columns';
-  import Column from '@site/src/components/Column';
-  export default {
-    // Reusing the default mapping
-    ...MDXComponents,
-    Columns,
-    Column,
-  };
\ No newline at end of file
+import React from "react";
+// eslint-disable-next-line
+import MDXComponents from "@theme-original/MDXComponents";
+import GlossaryTerm from "@site/src/components/GlossaryTerm";
+
+export default {
+  ...MDXComponents,
+  GlossaryTerm: GlossaryTerm,
+};
diff --git a/vcluster/_partials/what-are-virtual-clusters.mdx b/vcluster/_partials/what-are-virtual-clusters.mdx
index f514720f6..6e62ff026 100644
--- a/vcluster/_partials/what-are-virtual-clusters.mdx
+++ b/vcluster/_partials/what-are-virtual-clusters.mdx
@@ -1,4 +1,7 @@
-Virtual clusters are certified Kubernetes distributions that run as isolated, virtual environments nested inside a physical host cluster. They improve isolation and flexibility for multi-tenancy by allowing multiple teams to operate independently on the same infrastructure, minimizing conflicts, increasing autonomy, and reducing costs.
+import GlossaryTerm from '@site/src/components/GlossaryTerm'
+
+<!-- vale off -->
+<GlossaryTerm term="virtual-cluster">Virtual clusters</GlossaryTerm> are certified Kubernetes distributions that run as isolated, virtual environments nested inside a physical <GlossaryTerm term="host-cluster">host cluster</GlossaryTerm>. They improve isolation and flexibility for <GlossaryTerm term="multi-tenancy">multi-tenancy</GlossaryTerm> by allowing multiple teams to operate independently on the same infrastructure, minimizing conflicts, increasing autonomy, and reducing costs.
 
 <figure>
 <img
@@ -8,9 +11,9 @@ Virtual clusters are certified Kubernetes distributions that run as isolated, vi
   <figcaption>vCluster Architecture</figcaption>
 </figure>
 
-Virtual clusters run inside host cluster namespaces but operate as independent Kubernetes environments, each with its own API server, control plane, syncer, and set of resources. They share the physical infrastructure of the host cluster—such as CPU, memory, and storage—while managing their resources independently to support efficient utilization and scaling.
+Virtual clusters run inside host cluster namespaces but operate as independent Kubernetes environments, each with its own API server, control plane, <GlossaryTerm term="syncer">syncer</GlossaryTerm>, and set of resources.
 
-Although they rely on the host cluster for resource scheduling and networking, virtual clusters maintain a level of abstraction that isolates their operations from the host cluster’s global state.
+Although they rely on the host cluster for resource scheduling and networking, virtual clusters maintain a level of abstraction that isolates their operations from the host cluster's global state.
 
 ## Benefits
 Virtual clusters provide immense benefits for large-scale Kubernetes deployments and multi-tenancy.
@@ -25,9 +28,9 @@ Virtual clusters provide immense benefits for large-scale Kubernetes deployments
 
 ### Robust security and isolation
 
-  - **Granular permissions:** vCluster users operate with minimized permissions in the host cluster, significantly reducing the risk of privileged access misuse. Within their vCluster, users have admin-level control, enabling them to manage CRDs, RBAC, and other security policies independently.
-  - **Isolated control plane:** Each vCluster comes with its own dedicated API server and control plane, creating a strong isolation boundary.
-  - **Customizable security policies:** Tenants can implement additional vCluster-specific governance, including OPA policies, network policies, resource quotas, limit ranges, and admission control, in addition to the existing policies and security measures in the underlying physical host cluster.
+  - **Granular permissions:** <GlossaryTerm term="vcluster">vCluster</GlossaryTerm> users operate with minimized permissions in the host cluster
+  - **Isolated control plane:** Each virtual cluster comes with its own dedicated API server and control plane, creating a strong isolation boundary.
+  - **Customizable security policies:** Tenants can implement additional virtual cluster-specific governance, including OPA policies, network policies, resource quotas, limit ranges, and admission control, in addition to the existing policies and security measures in the underlying physical host cluster.
   - **Enhanced data protection:** With options for separate backing stores, including embedded SQLite, etcd, or external databases, virtual clusters allow for isolated data management, reducing the risk of data leakage between tenants.
 
 
@@ -47,9 +50,9 @@ Virtual clusters provide immense benefits for large-scale Kubernetes deployments
 
 ### Enhanced flexibility and compatibility
 
-  - **Diverse Kubernetes environments:** vCluster supports different Kubernetes versions and distributions (including Kubernetes, K3s, and K0s), allowing version skews. This makes it possible to tailor each virtual cluster to specific requirements without impacting others.
+  - **Diverse Kubernetes environments:** vCluster supports different Kubernetes versions and distributions (including Kubernetes, <GlossaryTerm term="k3s">K3s</GlossaryTerm>, and <GlossaryTerm term="k0s">K0s</GlossaryTerm>), allowing version skews. This makes it possible to tailor each virtual cluster to specific requirements without impacting others.
   - **Adaptable backing stores:** Choose from a range of data stores, from lightweight (SQLite) to enterprise-grade options (embedded etcd, external data stores like Global RDS), catering to various scalability and durability needs.
-  - **Runs anywhere:** Virtual clusters can run on EKS, GKE, AKS, OpenShift, RKE, K3s, cloud, edge, and on-prem. As long as it’s a Kubernetes cluster, you can run a virtual cluster on top of it.
+  - **Runs anywhere:** Virtual clusters can run on EKS, GKE, AKS, OpenShift, RKE, K3s, cloud, edge, and on-prem. As long as it's a Kubernetes cluster, you can run a virtual cluster on top of it.
 
 
 ### Improved scalability
@@ -69,3 +72,5 @@ Virtual clusters provide immense benefits for large-scale Kubernetes deployments
 
   - **Elevate your ISV offering with a dedicated cluster per customer:** Host each customer in a virtual cluster with strict tenant isolation and seamless scalability while consolidating essential tools into a unified platform stack serving multiple tenants.
   - **Build a managed Kubernetes service with best-in-class COGS and high margins:** Enable direct customer access to dedicated virtual Kubernetes clusters, streamlining node and resource allocation for industry-leading efficiency and unparalleled scalability.
+
+<!-- vale on -->
diff --git a/vcluster/introduction/what-are-virtual-clusters.mdx b/vcluster/introduction/what-are-virtual-clusters.mdx
index 91f140d67..aa5bac703 100644
--- a/vcluster/introduction/what-are-virtual-clusters.mdx
+++ b/vcluster/introduction/what-are-virtual-clusters.mdx
@@ -4,5 +4,6 @@ sidebar_position: 1
 ---
 
 import PartialWhatAreVirtualClusters from '../_partials/what-are-virtual-clusters.mdx'
+import GlossaryTerm from '@site/src/components/GlossaryTerm'
 
-<PartialWhatAreVirtualClusters />
+<PartialWhatAreVirtualClusters />
\ No newline at end of file
diff --git a/vcluster/reference/glossary.mdx b/vcluster/reference/glossary.mdx
new file mode 100644
index 000000000..c7c0b2b6b
--- /dev/null
+++ b/vcluster/reference/glossary.mdx
@@ -0,0 +1,36 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in vCluster documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+<!-- vale off -->
+# vCluster glossary {#vcluster-glossary}
+<!-- vale on -->
+
+This page contains definitions for common terms used throughout the vCluster documentation.
+
+<!-- vale off -->
+{Object.entries(glossaryData)
+  .filter(([key, entry]) => entry.type === 'vcluster')
+  .sort((a, b) => a[1].term.localeCompare(b[1].term))
+  .map(([key, entry]) => (
+    <div key={key} id={key} style={{ marginBottom: '2.5rem', borderBottom: '1px solid #eee', paddingBottom: '1.5rem' }}>
+      <h3>{entry.term}</h3>
+      <p>{entry.definition}</p>
+      
+      {entry.related && (
+        <p><strong>Related terms:</strong> {entry.related.map((term, index) => (
+          <span key={term}>
+            {index > 0 && ', '}
+            <span>{glossaryData[term]?.term || term}</span>
+          </span>
+        ))}</p>
+      )}
+    </div>
+  ))
+}
+<!-- vale on -->
\ No newline at end of file


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T12:08:56Z
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T12:15:14Z
user: login: sydorovdmytro
body: LGTM, but I'm, not that confident to review JS/CSS part 
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:13:10Z
user: login: joaniekube
body: I LOVE THIS! The most beautiful thing I have seen all week :)

I think a minor thing for consistency is to make the terms capitalized in the glossary and also if we can link to external links, like for K0s and K3s - that way we can improve back linking.

Also, super minor thing, but the related terms also have a hyphen.. so I guess it is using an id. Could that be resolved... again very very minor.

![image](https://github.com/user-attachments/assets/ac7800f9-0009-414a-b349-f06268f8817b)

state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:07:55Z
user: login: joaniekube
body: LGTM. This is cool
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:55:32Z

Review comments:
diff_hunk: @@ -0,0 +1,32 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in the platform documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+# Platform Glossary
path: platform/reference/glossary.mdx
user: login: github-actions[bot]
body: ⚠️ **[vale]** <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[Google.Headings] 'Platform Glossary' should use sentence-style capitalization.
created_at: 2025-05-21T12:08:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in vCluster documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+# vCluster Glossary
path: vcluster/reference/glossary.mdx
user: login: github-actions[bot]
body: ⚠️ **[vale]** <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[Google.Headings] 'vCluster Glossary' should use sentence-style capitalization.
created_at: 2025-05-21T12:08:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in vCluster documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+# vCluster Glossary
+
+This page contains definitions for common terms used throughout the vCluster documentation.
+
+{Object.entries(glossaryData)
+  .filter(([key, entry]) => entry.type === 'vcluster')
path: vcluster/reference/glossary.mdx
user: login: github-actions[bot]
body: 🚫 **[vale]** <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[Vale.Terms] Use 'vCluster' instead of 'vcluster'.
created_at: 2025-05-21T12:08:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,34 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in vCluster documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+# vCluster glossary {#vcluster-glossary}
path: vcluster/reference/glossary.mdx
user: login: github-actions[bot]
body: ⚠️ **[vale]** <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[Google.Headings] 'vCluster glossary {#vcluster-glossary}' should use sentence-style capitalization.
created_at: 2025-05-21T12:15:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,34 @@
+---
+title: Glossary
+sidebar_label: Glossary
+sidebar_position: 10
+description: Glossary of terms used in vCluster documentation
+---
+
+import glossaryData from '@site/src/data/glossary.yaml';
+
+# vCluster glossary {#vcluster-glossary}
path: vcluster/reference/glossary.mdx
user: login: github-actions[bot]
body: 🚫 **[vale]** <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[Vale.Terms] Use 'vCluster' instead of 'vcluster'.
created_at: 2025-05-21T12:15:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-05-21T12:05:36Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *vcluster-docs-site* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | b77890e7589b7625a0dd19f030f59781cb077308 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/projects/vcluster-docs-site/deploys/682de5245842d60008b25d6d |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-707--vcluster-docs-site.netlify.app](https://deploy-preview-707--vcluster-docs-site.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTcwNy0tdmNsdXN0ZXItZG9jcy1zaXRlLm5ldGxpZnkuYXBwIn0.6q_DYPehBckgnjhGbB2aXyMBzUHuhHuwCpgqkLCY7BY)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [vcluster-docs-site Preview](https://deploy-preview-707--vcluster-docs-site.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify project configuration](https://app.netlify.com/projects/vcluster-docs-site/configuration/notifications#deploy-notifications)._
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-21T12:05:58Z
body: [DOC-645: Hover over for explanations](https://linear.app/loft/issue/DOC-645/hover-over-for-explanations)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-21T12:09:34Z
body: [DOC-644: Add glossary to ensure consistent terms](https://linear.app/loft/issue/DOC-644/add-glossary-to-ensure-consistent-terms)
author_association: CONTRIBUTOR
user: login: Piotr1215
created_at: 2025-05-21T14:38:09Z
body: Awesome! Implemented the changes. One caveat is that related items cannot link cross-glossaries. Something I might implement later.
author_association: CONTRIBUTOR