diff --git a/BExIS.Modules.DQM.UI.Svelte/index.html b/BExIS.Modules.DQM.UI.Svelte/index.html
new file mode 100644
index 0000000..b607bfa
--- /dev/null
+++ b/BExIS.Modules.DQM.UI.Svelte/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ {#if error == true}
+
An error occurred.
+
+ Dataset does not contain structured primary data or the dataset is not public and you are not
+ logged in.
+
+ {/if}
+ {#if loading == true}
+
+
+
Loading
+
{props.value}%
+
+
+ {:else}{/if}
+
1. Duplicate Check
+
+ {#if duplicate_percent == 0}
+
+ {/if}
+ {#if duplicate_percent <= 10 && duplicate_percent > 0}
+
+ {/if}
+ {#if duplicate_percent > 10}
+
+ {/if}
+
+
+
+
+
+
2. Missing Value Check
+
+ Graph
+ Table
+
+
+
+ {#if affectedVariablen && affectedVariablen.length > 0}
+
+ | Variable Name | Unit | Count NA | Count Null |
+ {#each affectedVariablen as variable}
+
+ | {variable.variableName} |
+ {variable.unit || 'none'} |
+ {variable.NA ?? 0} |
+ {variable.NULL ?? 0} |
+
+ {/each}
+
+ {/if}
+
+
+
+
+ 3. Distribution & Count of Unique Values
+
+
+
+
+ Bubble Plot (#){count_number}
+
+ Bar Chart{count_text}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BExIS.Modules.DQM.UI.Svelte/src/app.css b/BExIS.Modules.DQM.UI.Svelte/src/app.css
new file mode 100644
index 0000000..79ce83c
--- /dev/null
+++ b/BExIS.Modules.DQM.UI.Svelte/src/app.css
@@ -0,0 +1,4 @@
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/BExIS.Modules.DQM.UI.Svelte/src/app.postcss b/BExIS.Modules.DQM.UI.Svelte/src/app.postcss
new file mode 100644
index 0000000..bd6213e
--- /dev/null
+++ b/BExIS.Modules.DQM.UI.Svelte/src/app.postcss
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/BExIS.Modules.DQM.UI.Svelte/src/draw_charts.js b/BExIS.Modules.DQM.UI.Svelte/src/draw_charts.js
new file mode 100644
index 0000000..092be39
--- /dev/null
+++ b/BExIS.Modules.DQM.UI.Svelte/src/draw_charts.js
@@ -0,0 +1,1127 @@
+/*
+ Everything you need to install and use Chart.js can be found here:
+ https://chartjs-plugin-datalabels.netlify.app
+ https://www.chartjs.org
+
+ Boxplot-Github:
+ https://github.com/sgratzl/chartjs-chart-boxplot
+ */
+import {
+ Chart,
+ LinearScale,
+ CategoryScale,
+ PieController,
+ BubbleController,
+ BarController,
+ LogarithmicScale,
+ ArcElement,
+ PointElement,
+ BarElement,
+ Decimation,
+ Filler,
+ Legend,
+ Title,
+ Tooltip
+} from 'chart.js';
+
+import { BoxPlotController, BoxAndWiskers } from '@sgratzl/chartjs-chart-boxplot';
+
+// @ts-ignore
+Chart.defaults.color = 'white';
+
+// REMOVE double scaling: let Chart.js handle DPR itself
+// Chart.defaults.devicePixelRatio = window.devicePixelRatio || 1;
+
+// @ts-ignore
+Chart.register(
+ BoxPlotController,
+ BoxAndWiskers,
+ LinearScale,
+ CategoryScale,
+ PieController,
+ BubbleController,
+ BarController,
+ LogarithmicScale,
+ ArcElement,
+ PointElement,
+ BarElement,
+ Decimation,
+ Filler,
+ Legend,
+ Title,
+ Tooltip
+);
+// Globale Font-Einstellungen - ERHÖHEN
+Chart.defaults.font.size = 18; // Von 16 auf 18
+Chart.defaults.font.family = 'Arial, sans-serif';
+Chart.defaults.font.weight = 'bold'; // NEU: Fett für bessere Lesbarkeit
+
+/**
+ * @param {{ count: any; countRows?: number; countColumns?: number; countData: any; countMv: any; countNull: any; missingValues?: any[]; affectedVariablen?: any[]; allVariablen?: any[]; duplicates?: any[]; }} d
+ * @param {HTMLElement | null} pieDiv
+ */
+
+export function prepareCanvas(canvas, width = 800, height = 500) {
+ // Set CSS size (visual) and the drawing buffer to the SAME size.
+ // Chart.js will apply its own DPR scaling for crisp text.
+ canvas.style.width = width + "px";
+ canvas.style.height = height + "px";
+ canvas.width = width;
+ canvas.height = height;
+ return canvas.getContext("2d");
+}
+
+// TextZoom-Plugin: vergrößert nur Text im Canvas, ohne Canvas/Chart zu skalieren
+const TextZoomPlugin = {
+ id: 'textZoom',
+ beforeInit(chart, _args, opts) {
+ const factor = Math.max(1, (opts && opts.factor) || 1.5);
+ const ctx = chart.ctx;
+ if (!ctx || ctx.__textZoomApplied) return;
+ ctx.__textZoomApplied = true;
+
+ const origFill = ctx.fillText.bind(ctx);
+ const origStroke = ctx.strokeText.bind(ctx);
+
+ ctx.fillText = function (text, x, y, maxWidth) {
+ this.save();
+ this.scale(factor, factor);
+ origFill(text, x / factor, y / factor, maxWidth ? maxWidth / factor : undefined);
+ this.restore();
+ };
+ ctx.strokeText = function (text, x, y, maxWidth) {
+ this.save();
+ this.scale(factor, factor);
+ origStroke(text, x / factor, y / factor, maxWidth ? maxWidth / factor : undefined);
+ this.restore();
+ };
+ }
+};
+
+Chart.register(TextZoomPlugin);
+
+// HTML-Legende: Box oben, Text darunter
+const htmlLegendPlugin = {
+ id: 'htmlLegend',
+ afterUpdate(chart, _args, options) {
+ const container = options.container;
+ if (!container) return;
+
+ // Container leeren
+ container.innerHTML = '';
+
+ // Legendendaten wie bisher über generateLabels holen
+ const items = chart.options.plugins.legend.labels.generateLabels(chart);
+
+ items.forEach((item) => {
+ const entry = document.createElement('div');
+ entry.style.display = 'flex';
+ entry.style.flexDirection = 'column';
+ entry.style.alignItems = 'center';
+ entry.style.marginRight = '16px';
+ entry.style.fontSize = '18px';
+ entry.style.fontWeight = 'bold';
+ entry.style.cursor = 'pointer';
+
+ // Farbbox
+ const box = document.createElement('div');
+ box.style.width = '40px';
+ box.style.height = '24px';
+ box.style.border = '1px solid #333';
+ box.style.background = item.fillStyle;
+
+ // Text UNTER der Box
+ const label = document.createElement('div');
+ if (Array.isArray(item.text)) {
+ // falls du wie aktuell [label, "77%"] zurückgibst
+ label.innerHTML = item.text.join('