diff --git a/Taskfile.yml b/Taskfile.yml
index 400b793ac888e68638fe5cd54cc79d66b40865f5..650cafaa4673f8b424b2a32a8df076d120f66f74 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -81,6 +81,14 @@ tasks:
     cmds:
       - format-and-lint-code
 
+  create-new-component:
+    silent: true
+    desc: Create a new component
+    aliases:
+      - cnc
+    cmds:
+      - create-new-component
+
   build-and-publish:
     silent: true
     desc: Build the app and publish it to the npm registry
diff --git a/development/issues/open/184.html b/development/issues/closed/184.html
similarity index 100%
rename from development/issues/open/184.html
rename to development/issues/closed/184.html
diff --git a/development/issues/open/184.mjs b/development/issues/closed/184.mjs
similarity index 100%
rename from development/issues/open/184.mjs
rename to development/issues/closed/184.mjs
diff --git a/development/issues/open/186.html b/development/issues/open/186.html
new file mode 100644
index 0000000000000000000000000000000000000000..3eca59ae2153bad4a30746aae1f7dbc3eeaa827c
--- /dev/null
+++ b/development/issues/open/186.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>add new field set for forms #186</title>
+  <script src="./186.mjs" type="module"></script>
+</head>
+<body>
+    <h1>add new field set for forms #186</h1>
+    <p></p>
+    <ul>
+        <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/186">Issue #186</a></li>
+        <li><a href="/">Back to overview</a></li>
+    </ul>
+    <main>
+          <!-- Write your code here -->
+    </main>
+
+</body>
+</html>
diff --git a/development/issues/open/186.mjs b/development/issues/open/186.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..763aa20e8306645f8ea954c208d1e710263f147c
--- /dev/null
+++ b/development/issues/open/186.mjs
@@ -0,0 +1,11 @@
+/**
+* @file development/issues/open/186.mjs
+* @url https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/186
+* @description add new field set for forms
+* @issue 186
+*/
+
+import "../../../source/components/style/property.pcss";
+import "../../../source/components/style/normalize.pcss";
+import "../../../source/components/style/typography.pcss";
+
diff --git a/development/issues/open/video.html b/development/issues/open/video.html
deleted file mode 100644
index 3903ddb82771f6797eb84a18632e3fe76393bd94..0000000000000000000000000000000000000000
--- a/development/issues/open/video.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Split Screen</title>
-    <script type="module">
-        import "../../../source/components/style/property.pcss";
-        import "../../../source/components/style/normalize.pcss";
-        import "../../../source/components/style/typography.pcss";
-        import "../../../source/components/style/color.pcss";
-        import "../../../source/components/layout/panel.mjs";
-        import "../../../source/components/layout/split-panel.mjs";
-    </script>
-    
-    <style>
-        *:not(:defined) {
-            visibility: hidden;
-        }
-    </style>
-</head>
-<body>
-<h1>Split Screen</h1>
-<monster-panel data-monster-option-heightadjustment="-1">
-    <monster-split-panel data-monster-option-splittype="vertical">
-        <monster-panel data-monster-option-heightadjustment="-3" slot="start">
-            <h1>Start Panel</h1>
-            <p>Some content x1</p>
-        </monster-panel>
-        <monster-panel data-monster-option-heightadjustment="-3" slot="end">
-            <h1>End Panel</h1>
-            <p>Some content x1</p>
-        </monster-panel>
-    </monster-split-panel>  
-</monster-panel>
-</body>
-</html>
diff --git a/development/scripts/createNewComponentClass.mjs b/development/scripts/createNewComponentClass.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..22c24ea4cbc0cdc5b42b1d566c507467e47b2f39
--- /dev/null
+++ b/development/scripts/createNewComponentClass.mjs
@@ -0,0 +1,441 @@
+import {sourcePath, projectRoot, license} from "./import.mjs";
+import {writeFileSync, readFileSync, mkdirSync, existsSync} from "fs";
+import {buildCSS, createScriptFilenameFromStyleFilename} from "./buildStylePostCSS.mjs";
+
+
+let version = "1.0.0";
+try {
+    const data = readFileSync(projectRoot + "/package.json", "utf8");
+    version = JSON.parse(data).version;
+} catch (e) {
+    console.error(e);
+    process.exit(1);
+
+}
+
+// small hack to get the version from the package.json, should be replaced with a proper version from git
+const versionParts = version.split('.');
+versionParts[1] = parseInt(versionParts[1]) + 1;
+versionParts[2] = 0;    
+const nextVersion = versionParts.join('.');
+
+
+const template = `${license}import { instanceSymbol } from "{{BACK_TO_ROOT_PATH}}/constants.mjs";
+import { addAttributeToken } from "{{BACK_TO_ROOT_PATH}}/dom/attributes.mjs";
+import {
+	ATTRIBUTE_ERRORMESSAGE,
+	ATTRIBUTE_ROLE,
+} from "{{BACK_TO_ROOT_PATH}}/dom/constants.mjs";
+import { CustomControl } from "{{BACK_TO_ROOT_PATH}}/dom/customcontrol.mjs";
+import {
+	assembleMethodSymbol,
+	registerCustomElement,
+} from "{{BACK_TO_ROOT_PATH}}/dom/customelement.mjs";
+import { findTargetElementFromEvent } from "{{BACK_TO_ROOT_PATH}}/dom/events.mjs";
+import { isFunction } from "{{BACK_TO_ROOT_PATH}}/types/is.mjs";
+import { {{CLASSNAME}}StyleSheet } from "./stylesheet/{{HTML_TAG_SUFFIX}}.mjs";
+import { fireCustomEvent } from "{{BACK_TO_ROOT_PATH}}/dom/events.mjs";
+
+export { {{CLASSNAME}} };
+
+/**
+ * @private
+ * @type {symbol}
+ */
+export const {{CLASSNAME_LOWER_FIRST}}ElementSymbol = Symbol("{{CLASSNAME_LOWER_FIRST}}Element");
+
+/**
+ * This CustomControl creates a {{CLASSNAME}} element with a variety of options.
+ *
+ * <img src="./images/{{HTML_TAG_SUFFIX}}.png">
+ *
+ * You can create this control either by specifying the HTML tag <monster-{{HTML_TAG_SUFFIX}} />\` directly in the HTML or using
+ * Javascript via the \`document.createElement('monster-{{HTML_TAG_SUFFIX}}');\` method.
+ *
+ * \`\`\`html
+ * <monster-{{HTML_TAG_SUFFIX}}></monster-{{HTML_TAG_SUFFIX}}>
+ * \`\`\`
+ *
+ * Or you can create this CustomControl directly in Javascript:
+ *
+ * \`\`\`js
+ * import {{{CLASSNAME}}} from '@schukai/monster/source/{{NAMESPACE_AS_PATH}}/{{HTML_TAG_SUFFIX}}.mjs';
+ * document.createElement('monster-{{HTML_TAG_SUFFIX}}');
+ * \`\`\`
+ *
+ * @externalExample {{BACK_TO_ROOT_PATH}}../example/{{NAMESPACE_AS_PATH}}/{{HTML_TAG_SUFFIX}}.mjs
+ * @startuml {{HTML_TAG_SUFFIX}}.png
+ * skinparam monochrome true
+ * skinparam shadowing false
+ * {{UML}}
+ * @enduml
+ *
+ * @since {{VERSION}}
+ * @copyright schukai GmbH
+ * @memberOf Monster.{{NAMESPACE_WITH_DOTS}}
+ * @summary A simple {{CLASSNAME}}
+ */
+class {{CLASSNAME}} extends {{PARENT_CLASS}} {
+	/**
+	 * This method is called by the \`instanceof\` operator.
+	 * @returns {symbol}
+	 */
+	static get [instanceSymbol]() {
+		return Symbol.for("@schukai/monster/{{NAMESPACE_AS_PATH}}/{{CLASSNAME_LOWER}}@@instance");
+	}
+
+	/**
+	 *
+	 * @return {{{NAMESPACE_WITH_DOTS}}.{{CLASSNAME}}
+	 */
+	[assembleMethodSymbol]() {
+		super[assembleMethodSymbol]();
+		initControlReferences.call(this);
+		initEventHandler.call(this);
+		return this;
+	}
+
+	/**
+	 * To set the options via the html tag the attribute \`data-monster-options\` must be used.
+	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
+	 *
+	 * The individual configuration values can be found in the table.
+	 *
+	 * @property {Object} templates Template definitions
+	 * @property {string} templates.main Main template
+	 * @property {Object} labels Label definitions
+	 * @property {Object} actions Callbacks
+	 * @property {string} actions.click="throw Error" Callback when clicked
+	 * @property {Object} features Features
+	 * @property {Object} classes CSS classes
+	 * @property {boolean} disabled=false Disabled state
+	 */
+	get defaults() {
+		return Object.assign({}, super.defaults, {
+			templates: {
+				main: getTemplate(),
+			},
+			labels: {
+			},
+			classes: {
+			},
+			disabled: false,
+			features: {
+            },
+			actions: {
+				click: () => {
+					throw new Error("the click action is not defined");
+				},
+			}{{VALUE_DEFAULTS}}
+		});
+	}
+
+	/**
+	 *
+	 * @return {string}
+	 */
+	static getTag() {
+		return "monster-{{HTML_TAG_SUFFIX}}";
+	}
+
+	/**
+	 *
+	 * @return {Array<CSSStyleSheet>}
+	 */
+	static getCSSStyleSheet() {
+		return [{{CLASSNAME}}StyleSheet];
+	}
+	
+	{{HTML_ELEMENT_FORM_INTERNALS}}
+}
+
+/**
+ * @private
+ * @return {initEventHandler}
+ * @fires Monster.Components.Form.event:monster-{{HTML_TAG_SUFFIX}}-clicked
+ */
+function initEventHandler() {
+	const self = this;
+	const element = this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol];
+
+	const type = "click";
+
+	element.addEventListener(type, function (event) {
+		const callback = self.getOption("actions.click");
+
+		fireCustomEvent(self, "monster-{{HTML_TAG_SUFFIX}}-clicked", {
+			element: self,
+		});
+
+		if (!isFunction(callback)) {
+			return;
+		}
+
+		const element = findTargetElementFromEvent(
+			event,
+			ATTRIBUTE_ROLE,
+			"control",
+		);
+
+		if (!(element instanceof Node && self.hasNode(element))) {
+			return;
+		}
+
+		callback.call(self, event);
+	});
+
+	return this;
+}
+
+/**
+ * @private
+ */
+function initControlReferences() {
+	this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol] = this.shadowRoot.querySelector(
+		\`[\${ATTRIBUTE_ROLE}="control"]\`,
+	);
+}
+
+/**
+ * @private
+ * @return {string}
+ */
+function getTemplate() {
+	// language=HTML
+	return \`
+        <div data-monster-role="control" part="control">
+        </div>\`;
+}
+
+
+registerCustomElement({{CLASSNAME}});
+`;
+
+const internalTemplate = `
+	/**
+	 * The {{CLASSNAME}}.click() method simulates a click on the internal element.
+	 *
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click}
+	 */
+	click() {
+		if (this.getOption("disabled") === true) {
+			return;
+		}
+
+		if (
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol] &&
+			isFunction(this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].click)
+		) {
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].click();
+		}
+	}
+
+	/**
+	 * The Button.focus() method sets focus on the internal element.
+	 *
+	 * @param {Object} options
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus}
+	 */
+	focus(options) {
+		if (this.getOption("disabled") === true) {
+			return;
+		}
+
+		if (
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol] &&
+			isFunction(this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].focus)
+		) {
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].focus(options);
+		}
+	}
+
+	/**
+	 * The Button.blur() method removes focus from the internal element.
+	 */
+	blur() {
+		if (
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol] &&
+			isFunction(this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].blur)
+		) {
+			this[{{CLASSNAME_LOWER_FIRST}}ElementSymbol].blur();
+		}
+	}
+	
+	/**
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
+	 * @return {boolean}
+	 */
+	static get formAssociated() {
+		return true;
+	}
+	
+	/**
+	 * The current value of the form control.
+	 *
+	 * \`\`\`js
+	 * e = document.querySelector('monster-{{HTML_TAG_SUFFIX}}');
+	 * console.log(e.value)
+	 * \`\`\`
+	 *
+	 * @property {string}
+	 */
+	get value() {
+		return this.getOption("value");
+	}
+
+	/**
+	 * Set value of the form control.
+	 *
+	 * \`\`\`
+	 * e = document.querySelector('monster-{{HTML_TAG_SUFFIX}}');
+	 * e.value=1
+	 * \`\`\`
+	 *
+	 * @property {string} value
+	 * @throws {Error} unsupported type
+	 */
+	set value(value) {
+		this.setOption("value", value);
+		try {
+			this?.setFormValue(this.value);
+		} catch (e) {
+			addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
+		}
+	}
+	
+`;
+
+
+const args = process.argv.slice(2);
+
+const argsObj = args.reduce((acc, arg) => {
+    let [key, value] = arg.split('=');
+    key = key.replace(/^--/, '');
+    acc[key] = value;
+    return acc;
+}, {});
+
+function printUsage() {
+    console.log("Usage: node createNewComponentClass.mjs --classname=YourClassName --namespace=components.yourNamespace");
+}
+
+
+if (!argsObj.classname) {
+    console.error("No classname provided, you can use --classname=YourClassName");
+    printUsage();
+    process.exit(1);
+}
+
+if (argsObj.classname.match(/[^A-Za-z0-9]/)) {
+    console.error("Classname can only contain letters and numbers");
+    process.exit(1);
+}
+
+if (argsObj.classname.match(/^[0-9]/)) {
+    console.error("Classname can not start with a number");
+    process.exit(1);
+}
+
+if (argsObj.classname.match(/^[a-z]/)) {
+    console.error("Classname should start with an uppercase letter");
+    process.exit(1);
+}
+
+
+if (!argsObj.namespace) {
+    console.error("No namespace provided, you can use --namespace=Your.Namespace");
+    printUsage();
+    process.exit(1);
+}
+
+if (argsObj.namespace.match(/[^A-Za-z0-9.]/)) {
+    console.error("Namespace can only contain letters, numbers and dots");
+    process.exit(1);
+}
+
+if (!argsObj.namespace.match(/^components/)) {
+    console.error("Namespace must start with components");
+    process.exit(1);
+}
+
+
+const lowerFirst = argsObj.classname[0].toLowerCase() + argsObj.classname.slice(1);
+const htmlTagSuffix = argsObj.classname.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
+
+const namespace = argsObj.namespace.split('.').map((part) => part[0].toUpperCase() + part.slice(1))
+const namespaceAsPath = namespace.join('/').toLowerCase();
+const namespaceWithDots = namespace.join('.');
+
+const directory = `${sourcePath}/${namespaceAsPath}`;
+const styleDirectory = `${directory}/style`;
+const filename = `${directory}/${htmlTagSuffix}.mjs`;
+const pcssFilename = `${styleDirectory}/${htmlTagSuffix}.pcss`;
+
+const exampleDirectory = `${projectRoot}/example/${namespaceAsPath}`;
+const exampleFilename = `${exampleDirectory}/${htmlTagSuffix}.mjs`;
+
+const backToRootPath = '../'.repeat(namespace.length).slice(0, -1);
+
+const isFormControl = argsObj.namespace.match(/components\.form/);
+const formInternalsCode = isFormControl ? internalTemplate : '';
+const parentClass = isFormControl ? 'CustomControl' : 'CustomElement';
+const valueDefaults = isFormControl ? ",\n            value: null" : "";
+
+const uml = isFormControl ? `HTMLElement <|-- CustomElement
+ * CustomElement <|-- CustomControl
+ * CustomControl <|-- {{CLASSNAME}}` : `HTMLElement <|-- CustomElement
+ * CustomElement <|-- {{CLASSNAME}}`;
+
+const code = template.replace(/{{HTML_ELEMENT_FORM_INTERNALS}}/g, formInternalsCode)
+    .replace(/{{UML}}/g, uml)
+    .replace(/{{CLASSNAME}}/g, argsObj.classname)
+    .replace(/{{CLASSNAME_LOWER_FIRST}}/g, lowerFirst)
+    .replace(/{{CLASSNAME_LOWER}}/g, argsObj.classname.toLowerCase())
+    .replace(/{{HTML_TAG_SUFFIX}}/g, htmlTagSuffix)
+    .replace(/{{NAMESPACE_AS_PATH}}/g, namespaceAsPath)
+    .replace(/{{BACK_TO_ROOT_PATH}}/g, backToRootPath)
+    .replace(/{{PARENT_CLASS}}/g, parentClass)
+    .replace(/{{VALUE_DEFAULTS}}/g, valueDefaults)
+
+    .replace(/{{VERSION}}/g, nextVersion) 
+    .replace(/{{NAMESPACE_WITH_DOTS}}/g, namespaceWithDots);
+
+try {
+    writeFileSync(filename, code);
+} catch (e) {
+    console.error(e);
+    process.exit(1);
+}
+
+const exampleCode = `import "@schukai/monster/source/${namespaceAsPath}/${htmlTagSuffix}.mjs";
+const element = document.createElement('monster-${htmlTagSuffix}');
+element.setOption('disable', 'false')
+document.body.appendChild(element);
+`;
+
+try {
+
+    if (!existsSync(exampleDirectory)) {
+        mkdirSync(exampleDirectory, {recursive: true});
+    }
+
+    writeFileSync(exampleFilename, exampleCode);
+} catch (e) {
+    console.error(e);
+    process.exit(1);
+}
+
+try {
+    writeFileSync(pcssFilename, '');
+} catch (e) {
+    console.error(e);
+    process.exit(1);
+}
+
+buildCSS(pcssFilename, createScriptFilenameFromStyleFilename(pcssFilename)).then(() => {
+    console.log(`Created ${filename} and ${pcssFilename}`);
+    process.exit(0);
+}).catch((e) => {
+    console.error(e);
+    process.exit(1);
+});
diff --git a/devenv.nix b/devenv.nix
index 647acd709b69e4fad0bc47e1818af81367fbd600..fd4d7a872b055ef92a4dd9eefaf0d0a8a8cee482 100644
--- a/devenv.nix
+++ b/devenv.nix
@@ -41,6 +41,8 @@
       // THIS FILE IS AUTOGENERATED. DO NOT EDIT THIS FILE DIRECTLY.
     '';
   };
+  
+  
 
   commonFunctionsScript = pkgs.writeScript "common-functions" ''
 
@@ -611,13 +613,27 @@ in {
     
     echo_section "build monster file"
     
-    if ! $(${pkgs.nodejs_20}/bin/node ${config.devenv.root}/development/scripts/buildMonsterFile.mjs) 
+    if ! ${pkgs.nodejs_20}/bin/node ${config.devenv.root}/development/scripts/buildMonsterFile.mjs 
     then
-      echo "ERROR: script buildMonsterFile.mjs failed, check your JS!"
+      echo_fail "script buildMonsterFile.mjs failed, check your JS!"
       exit 1
     fi
     echo_ok "Monster file created"
   '';
+  
+  scripts.create-new-component-class.exec = ''
+    #!${pkgs.bash}/bin/bash
+    source ${commonFunctionsScript}
+    
+    echo_section "create new component class"
+    
+    if ! ${pkgs.nodejs_20}/bin/node ${config.devenv.root}/development/scripts/createNewComponentClass.mjs "$@" 
+    then
+      echo_fail "script createNewClass.mjs failed, check your JS!"
+      exit 1
+    fi
+    echo_ok "New component class created"
+  '';
 
   scripts.run-web-tests.exec = ''
     PROJECT_ROOT="${config.devenv.root}"
diff --git a/example/components/form/field-set.mjs b/example/components/form/field-set.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..e87ab75d01c37df302df6520ffe3e13ba7c1cb7a
--- /dev/null
+++ b/example/components/form/field-set.mjs
@@ -0,0 +1,4 @@
+import "@schukai/monster/source/components/form/field-set.mjs";
+const element = document.createElement('monster-field-set');
+element.setOption('disable', 'false')
+document.body.appendChild(element);
diff --git a/example/components/form/select.mjs b/example/components/form/select.mjs
index 808e47ee2ef645b042e95ed92818f719becffc2f..04bae4dc95c976bb9c10fb07cb40fedc1fe556db 100644
--- a/example/components/form/select.mjs
+++ b/example/components/form/select.mjs
@@ -1,4 +1,4 @@
-import {Select} from '@schukai/component-form/source/select.js';
+import '@schukai/component-form/source/select.js';
 
 const select = document.createElement('monster-select');
 select.setOption('mapping.labelTemplate', '${name} (${alpha-2})')
diff --git a/opt/scripts/build-stylesheets.cjs b/opt/scripts/build-stylesheets.cjs
deleted file mode 100644
index ba7037bb3cd8584969c70e89421e0009853e4f9f..0000000000000000000000000000000000000000
--- a/opt/scripts/build-stylesheets.cjs
+++ /dev/null
@@ -1,158 +0,0 @@
-// const fs = require('fs');
-// const path = require('path');
-// const args = process.argv.slice(2);
-//
-// if (args.length < 1) {
-//     console.log("Usage: node build-stylesheets.js <project-root>");
-//     process.exit(1);
-// }
-//
-// const projectRoot = args[0];
-//
-// if (!fs.existsSync(projectRoot)) {
-//     console.log("Project root " + projectRoot + " does not exist");
-//     process.exit(1);
-// }
-//
-// let relPath = null;
-// for (let i = 0; i < args.length; i++) {
-//     if (args[i] === '--rel-path') {
-//         relPath = args[i + 1];
-//     }
-// }
-//
-// if (relPath === null || relPath === undefined || relPath === "") {
-//     console.error("Relativ path must be set");
-//     console.log("Usage: node build-stylesheets.js --relPath <relativ-path>");
-//     process.exit(1);
-// }
-//
-// if (relPath.substr(-1) !== '/') {
-//     relPath += '/';
-// }
-//
-// if (relPath.substr(0, 1) === '/') {
-//     console.error("Relativ path must not start with /");
-//     console.log("Usage: node build-stylesheets.js --relPath <relativ-path>");
-//     process.exit(1);
-// }
-//
-// // build rel, return path ../ to root
-// let backToRootPath = "";
-// const parts = relPath.split("/");
-// for (let i = 0; i < parts.length; i++) {
-//     backToRootPath += "../";
-// }
-//
-// const absoluteProjectRoot = path.resolve(projectRoot) + "/";
-// const absoluteSourcePath = absoluteProjectRoot + 'source/';
-// const styleSourceRoot = absoluteSourcePath + relPath + 'style/'
-// const styleSheetRoot = absoluteSourcePath + relPath + 'stylesheet/'
-// const nodeModuleStyleSourceRoot = absoluteProjectRoot + 'node_modules/'
-//
-// if (!fs.existsSync(styleSourceRoot)) {
-//     console.log("Style source root " + styleSourceRoot + " does not exist");
-//     process.exit(1);
-// }
-//
-// if (!fs.existsSync(nodeModuleStyleSourceRoot)) {
-//     console.log("Node module style source root " + nodeModuleStyleSourceRoot + " does not exist");
-//     process.exit(1);
-// }
-//
-// if (!fs.existsSync(styleSheetRoot)) {
-//     fs.mkdirSync(styleSheetRoot);
-// }
-//
-// let promises = [];
-// let styles = new Map();
-//
-//
-// export function buildScriptCSS(styleSheetRoot, styles) {
-//     styles.forEach((css, fn) => {
-//
-//         let className = path.parse(fn).name;
-//         className = className.charAt(0).toUpperCase() + className.slice(1);
-//         className = className.replace(/-([a-z])/g, function (g) {
-//             return g[1].toUpperCase();
-//         });
-//         let layerName = className.toLowerCase();
-//
-//         css = css.replace(/"/g, '\\"');
-//         const code = codeTemplate
-//             .replaceAll("{{backToRootPath}}", path.relative(styleSheetRoot, projectRoot) + "/")
-//             .replaceAll("{{copyRightYear}}", new Date().getFullYear())
-//             .replaceAll("{{ClassName}}", className)
-//             .replaceAll("{{LayerName}}", layerName)
-//             .replaceAll("{{css}}", css);
-//
-//         const targetFile = path.normalize(path.join(styleSheetRoot, fn));
-//         fs.mkdirSync(path.dirname(targetFile), {recursive: true});
-//         fs.writeFileSync(targetFile, code);
-//     })
-// }
-//
-//
-// function scanFiles(root, keyPath) {
-//
-//     if (keyPath === undefined) {
-//         keyPath = [];
-//     }
-//
-//     return new Promise((resolve, reject) => {
-//
-//         let localPromises = [];
-//         let f;
-//
-//         const dir = fs.opendirSync(root);
-//         while ((f = dir.readSync()) !== null) {
-//
-//             const key = f.name;
-//             const fn = path.join(root, f.name);
-//             const currentPath = [...keyPath]
-//             currentPath.push(key);
-//
-//             if (f.isDirectory()) {
-//
-//                 if (["node_modules", "mixin"].includes(f.name)) {
-//                     continue;
-//                 }
-//
-//                 localPromises.push(scanFiles(fn, currentPath));
-//                 continue;
-//             } else if (!f.isFile()) {
-//                 continue;
-//             }
-//
-//             if ((path.extname(f.name) !== ".css" && path.extname(f.name) !== ".pcss")) {
-//                 continue;
-//             }
-//
-//             //console.log("Found file: " + fn);
-//             let content = fs.readFileSync(fn, 'utf8');
-//             localPromises.push(new Promise((resolve, reject) => {
-//                 (async () => {
-//                     const {rewriteCSS} = await import('./rewriteCSS.mjs');
-//                     resolve(rewriteCSS(styles, content, fn, currentPath, absoluteSourcePath, nodeModuleStyleSourceRoot, styleSourceRoot, absoluteProjectRoot));
-//
-//                 })();
-//             }));
-//
-//         }
-//
-//         dir.closeSync();
-//
-//         Promise.all(localPromises).then(resolve).catch(reject);
-//
-//     })
-// }
-//
-//
-// scanFiles(path.normalize(styleSourceRoot)).then(() => {
-//
-//     buildScriptCSS(styleSheetRoot, styles);
-//
-// }).catch(e => {
-//     console.log("Error creating stylesheets");
-//     console.error(e);
-// })
diff --git a/source/components/form/field-set.mjs b/source/components/form/field-set.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..fdb7cb325286b34470211be067d0c9abb3ed0df8
--- /dev/null
+++ b/source/components/form/field-set.mjs
@@ -0,0 +1,296 @@
+/**
+ * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
+ * Node module: @schukai/monster
+ *
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
+ *
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
+ * For more information about purchasing a commercial license, please contact schukai GmbH.
+ */
+
+import { instanceSymbol } from "../../constants.mjs";
+import { addAttributeToken } from "../../dom/attributes.mjs";
+import {
+	ATTRIBUTE_ERRORMESSAGE,
+	ATTRIBUTE_ROLE,
+} from "../../dom/constants.mjs";
+import { CustomControl } from "../../dom/customcontrol.mjs";
+import {
+	assembleMethodSymbol,
+	registerCustomElement,
+} from "../../dom/customelement.mjs";
+import { findTargetElementFromEvent } from "../../dom/events.mjs";
+import { isFunction } from "../../types/is.mjs";
+import { FieldSetStyleSheet } from "./stylesheet/field-set.mjs";
+import { fireCustomEvent } from "../../dom/events.mjs";
+
+export { FieldSet };
+
+/**
+ * @private
+ * @type {symbol}
+ */
+export const fieldSetElementSymbol = Symbol("fieldSetElement");
+
+/**
+ * This CustomControl creates a FieldSet element with a variety of options.
+ *
+ * <img src="./images/field-set.png">
+ *
+ * You can create this control either by specifying the HTML tag <monster-field-set />` directly in the HTML or using
+ * Javascript via the `document.createElement('monster-field-set');` method.
+ *
+ * ```html
+ * <monster-field-set></monster-field-set>
+ * ```
+ *
+ * Or you can create this CustomControl directly in Javascript:
+ *
+ * ```js
+ * import {FieldSet} from '@schukai/monster/source/components/form/field-set.mjs';
+ * document.createElement('monster-field-set');
+ * ```
+ *
+ * @externalExample ../..../example/components/form/field-set.mjs
+ * @startuml field-set.png
+ * skinparam monochrome true
+ * skinparam shadowing false
+ * HTMLElement <|-- CustomElement
+ * CustomElement <|-- CustomControl
+ * CustomControl <|-- FieldSet
+ * @enduml
+ *
+ * @since 3.65.0
+ * @copyright schukai GmbH
+ * @memberOf Monster.Components.Form
+ * @summary A simple FieldSet
+ */
+class FieldSet extends CustomControl {
+	/**
+	 * This method is called by the `instanceof` operator.
+	 * @returns {symbol}
+	 */
+	static get [instanceSymbol]() {
+		return Symbol.for("@schukai/monster/components/form/fieldset@@instance");
+	}
+
+	/**
+	 *
+	 * @return {Components.Form.FieldSet
+	 */
+	[assembleMethodSymbol]() {
+		super[assembleMethodSymbol]();
+		initControlReferences.call(this);
+		initEventHandler.call(this);
+		return this;
+	}
+
+	/**
+	 * To set the options via the html tag the attribute `data-monster-options` must be used.
+	 * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
+	 *
+	 * The individual configuration values can be found in the table.
+	 *
+	 * @property {Object} templates Template definitions
+	 * @property {string} templates.main Main template
+	 * @property {Object} labels Label definitions
+	 * @property {Object} actions Callbacks
+	 * @property {string} actions.click="throw Error" Callback when clicked
+	 * @property {Object} features Features
+	 * @property {Object} classes CSS classes
+	 * @property {boolean} disabled=false Disabled state
+	 */
+	get defaults() {
+		return Object.assign({}, super.defaults, {
+			templates: {
+				main: getTemplate(),
+			},
+			labels: {
+			},
+			classes: {
+			},
+			disabled: false,
+			features: {
+            },
+			actions: {
+				click: () => {
+					throw new Error("the click action is not defined");
+				},
+			},
+            value: null
+		});
+	}
+
+	/**
+	 *
+	 * @return {string}
+	 */
+	static getTag() {
+		return "monster-field-set";
+	}
+
+	/**
+	 *
+	 * @return {Array<CSSStyleSheet>}
+	 */
+	static getCSSStyleSheet() {
+		return [FieldSetStyleSheet];
+	}
+	
+	
+	/**
+	 * The FieldSet.click() method simulates a click on the internal element.
+	 *
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click}
+	 */
+	click() {
+		if (this.getOption("disabled") === true) {
+			return;
+		}
+
+		if (
+			this[fieldSetElementSymbol] &&
+			isFunction(this[fieldSetElementSymbol].click)
+		) {
+			this[fieldSetElementSymbol].click();
+		}
+	}
+
+	/**
+	 * The Button.focus() method sets focus on the internal element.
+	 *
+	 * @param {Object} options
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus}
+	 */
+	focus(options) {
+		if (this.getOption("disabled") === true) {
+			return;
+		}
+
+		if (
+			this[fieldSetElementSymbol] &&
+			isFunction(this[fieldSetElementSymbol].focus)
+		) {
+			this[fieldSetElementSymbol].focus(options);
+		}
+	}
+
+	/**
+	 * The Button.blur() method removes focus from the internal element.
+	 */
+	blur() {
+		if (
+			this[fieldSetElementSymbol] &&
+			isFunction(this[fieldSetElementSymbol].blur)
+		) {
+			this[fieldSetElementSymbol].blur();
+		}
+	}
+	
+	/**
+	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
+	 * @return {boolean}
+	 */
+	static get formAssociated() {
+		return true;
+	}
+	
+	/**
+	 * The current value of the form control.
+	 *
+	 * ```js
+	 * e = document.querySelector('monster-field-set');
+	 * console.log(e.value)
+	 * ```
+	 *
+	 * @property {string}
+	 */
+	get value() {
+		return this.getOption("value");
+	}
+
+	/**
+	 * Set value of the form control.
+	 *
+	 * ```
+	 * e = document.querySelector('monster-field-set');
+	 * e.value=1
+	 * ```
+	 *
+	 * @property {string} value
+	 * @throws {Error} unsupported type
+	 */
+	set value(value) {
+		this.setOption("value", value);
+		try {
+			this?.setFormValue(this.value);
+		} catch (e) {
+			addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
+		}
+	}
+	
+
+}
+
+/**
+ * @private
+ * @return {initEventHandler}
+ * @fires Monster.Components.Form.event:monster-field-set-clicked
+ */
+function initEventHandler() {
+	const self = this;
+	const element = this[fieldSetElementSymbol];
+
+	const type = "click";
+
+	element.addEventListener(type, function (event) {
+		const callback = self.getOption("actions.click");
+
+		fireCustomEvent(self, "monster-field-set-clicked", {
+			element: self,
+		});
+
+		if (!isFunction(callback)) {
+			return;
+		}
+
+		const element = findTargetElementFromEvent(
+			event,
+			ATTRIBUTE_ROLE,
+			"control",
+		);
+
+		if (!(element instanceof Node && self.hasNode(element))) {
+			return;
+		}
+
+		callback.call(self, event);
+	});
+
+	return this;
+}
+
+/**
+ * @private
+ */
+function initControlReferences() {
+	this[fieldSetElementSymbol] = this.shadowRoot.querySelector(
+		`[${ATTRIBUTE_ROLE}="control"]`,
+	);
+}
+
+/**
+ * @private
+ * @return {string}
+ */
+function getTemplate() {
+	// language=HTML
+	return `
+        <div data-monster-role="control" part="control">
+        </div>`;
+}
+
+
+registerCustomElement(FieldSet);
diff --git a/source/components/form/style/field-set.pcss b/source/components/form/style/field-set.pcss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/source/components/form/stylesheet/field-set.mjs b/source/components/form/stylesheet/field-set.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..46532ef239f18b76aba95bc83f4dcc5e1d3f812d
--- /dev/null
+++ b/source/components/form/stylesheet/field-set.mjs
@@ -0,0 +1,31 @@
+/**
+ * Copyright © schukai GmbH and all contributing authors, 2024. All rights reserved.
+ * Node module: @schukai/monster
+ *
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
+ *
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
+ * For more information about purchasing a commercial license, please contact schukai GmbH.
+ */
+
+import {addAttributeToken} from "../../../dom/attributes.mjs";
+import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
+
+export {FieldSetStyleSheet}
+
+/**
+ * @private
+ * @type {CSSStyleSheet}
+ */
+const FieldSetStyleSheet = new CSSStyleSheet();
+
+try {
+  FieldSetStyleSheet.insertRule(`
+@layer fieldset { 
+ 
+}`, 0);
+} catch (e) {
+  addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
+}