diff --git a/application/source/dom/customcontrol.mjs b/application/source/dom/customcontrol.mjs
index 7cf06490f8fa4d2d5b6c69596de71e269b27f058..d884ef740947b0c7be40aabd6c21118c939e245c 100644
--- a/application/source/dom/customcontrol.mjs
+++ b/application/source/dom/customcontrol.mjs
@@ -5,11 +5,12 @@
  * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
  */
 
-import { extend } from "../data/extend.mjs";
-import { ATTRIBUTE_VALUE } from "./constants.mjs";
-import { CustomElement, attributeObserverSymbol } from "./customelement.mjs";
-import { instanceSymbol } from "../constants.mjs";
-export { CustomControl };
+import {extend} from "../data/extend.mjs";
+import {ATTRIBUTE_VALUE} from "./constants.mjs";
+import {CustomElement, attributeObserverSymbol} from "./customelement.mjs";
+import {instanceSymbol} from "../constants.mjs";
+
+export {CustomControl};
 
 /**
  * @private
@@ -41,6 +42,7 @@ const attachedInternalSymbol = Symbol("attachedInternal");
  * @see {@link https://www.npmjs.com/package/element-internals-polyfill}
  * @see {@link https://github.com/WICG/webcomponents}
  * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements}
+ * @see {@link https://html.spec.whatwg.org/dev/custom-elements.html#custom-element-reactions}
  * @license AGPLv3
  * @since 1.14.0
  * @copyright schukai GmbH
@@ -74,7 +76,7 @@ class CustomControl extends CustomElement {
      * @since 2.1.0
      */
     static get [instanceSymbol]() {
-        return Symbol.for("@schukai/monster/dom/custom-control");
+        return Symbol.for("@schukai/monster/dom/custom-control@@instance");
     }
 
     /**
@@ -84,20 +86,18 @@ class CustomControl extends CustomElement {
      * @since 1.15.0
      */
     static get observedAttributes() {
-        const list = super.observedAttributes;
-        list.push(ATTRIBUTE_VALUE);
-        return list;
+        return super.observedAttributes;
     }
 
     /**
+     * Adding a static formAssociated property, with a true value, makes an autonomous custom element a form-associated custom element.
      *
      * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
+     * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example}
      * @since 1.14.0
      * @return {boolean}
      */
-    static get formAssociated() {
-        return true;
-    }
+    static formAssociated = true
 
     /**
      * Derived classes can override and extend this method as follows.
@@ -298,6 +298,46 @@ class CustomControl extends CustomElement {
     reportValidity() {
         return getInternal.call(this)?.reportValidity();
     }
+
+    /**
+     * @param {string} form
+     */
+    formAssociatedCallback(form) {
+        if (form) {
+            if(form.id) {
+                this.setAttribute("form", form.id);    
+            }
+        } else {
+            this.removeAttribute("form");
+        }
+    }
+
+    /**
+     * @param {string} disabled
+     */
+    formDisabledCallback(disabled) {
+        if (disabled) {
+            this.setAttribute("disabled", "");
+        } else {
+            this.removeAttribute("disabled");
+        }
+    }
+
+    /**
+     * @param {string} state
+     * @param {string} mode
+     */
+    formStateRestoreCallback(state, mode) {
+        
+    }
+
+    /**
+     *
+     */
+    formResetCallback() {
+       this.value = "";
+    }
+
 }
 
 /**
@@ -313,7 +353,7 @@ function getInternal() {
         throw new Error("ElementInternals is not supported and a polyfill is necessary");
     }
 
-    return this[attachedInternalSymbol];
+    return self[attachedInternalSymbol];
 }
 
 /**
diff --git a/application/source/dom/customelement.mjs b/application/source/dom/customelement.mjs
index 8ff4866e51da8a8d15526911671a715f357fca29..9ce57bfa812b77d25df6591799a5af41fbde428d 100644
--- a/application/source/dom/customelement.mjs
+++ b/application/source/dom/customelement.mjs
@@ -31,6 +31,7 @@ import {instanceSymbol} from "../constants.mjs";
 import {getDocumentTranslations, Translations} from "../i18n/translations.mjs";
 import {getSlottedElements} from "./slotted.mjs";
 import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
+import {setOptionFromAttribute} from "./util/set-option-from-attribute.mjs";
 
 export {
     CustomElement,
@@ -220,7 +221,7 @@ class CustomElement extends HTMLElement {
      * @since 2.1.0
      */
     static get [instanceSymbol]() {
-        return Symbol.for("@schukai/monster/dom/custom-element");
+        return Symbol.for("@schukai/monster/dom/custom-element@@instance");
     }
 
     /**
@@ -232,7 +233,7 @@ class CustomElement extends HTMLElement {
      * @since 1.15.0
      */
     static get observedAttributes() {
-        return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED];
+        return [];
     }
 
     /**
@@ -309,7 +310,7 @@ class CustomElement extends HTMLElement {
      */
     get defaults() {
         return {
-            ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED),
+            disabled: false,
             shadowMode: "open",
             delegatesFocus: true,
             templates: {
@@ -605,8 +606,11 @@ class CustomElement extends HTMLElement {
     attributeChangedCallback(attrName, oldVal, newVal) {
         const self = this;
 
-        const callback = self[attributeObserverSymbol]?.[attrName];
+        if (attrName.startsWith("data-monster-option-")) {
+            setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"]) 
+        }
 
+        const callback = self[attributeObserverSymbol]?.[attrName];
         if (isFunction(callback)) {
             try {
                 callback.call(self, newVal, oldVal);
diff --git a/application/source/dom/util/extract-keys.mjs b/application/source/dom/util/extract-keys.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..d2a28d6dc8beb05f7131b856d66fe72ef9e206e2
--- /dev/null
+++ b/application/source/dom/util/extract-keys.mjs
@@ -0,0 +1,39 @@
+/**
+ * Copyright schukai GmbH and contributors 2023. All Rights Reserved.
+ * Node module: @schukai/monster
+ * This file is licensed under the AGPLv3 License.
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
+ */
+
+export {extractKeys}
+
+/**
+ * Extracts the keys from the given object and returns a map with the keys and values.
+ *
+ * @private
+ * @param {object} obj
+ * @param {string} keyPrefix
+ * @param {string} keySeparator
+ * @param {string} valueSeparator
+ * @returns {Map<any, any>}
+ */
+function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
+    const resultMap = new Map();
+
+    function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
+        for (const key in currentObj) {
+            if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
+                const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
+                const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
+                helper(currentObj[key], newKeyPrefix, newValuePrefix);
+            } else {
+                const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
+                const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
+                resultMap.set(finalKey, finalValue);
+            }
+        }
+    }
+
+    helper(obj, keyPrefix, keyPrefix);
+    return resultMap;
+}
\ No newline at end of file
diff --git a/application/source/dom/util/init-options-from-attributes.mjs b/application/source/dom/util/init-options-from-attributes.mjs
index 85664dee76277157bf71551935960d35a3ffb7b1..6f4c9d6419c36a36ec8618a9fc118c275d6c3e67 100644
--- a/application/source/dom/util/init-options-from-attributes.mjs
+++ b/application/source/dom/util/init-options-from-attributes.mjs
@@ -8,6 +8,7 @@
 import {Pathfinder} from '../../data/pathfinder.mjs';
 import {isFunction} from '../../types/is.mjs';
 import {attributeObserverSymbol} from "../customelement.mjs";
+import {extractKeys} from "./extract-keys.mjs";
 
 export {initOptionsFromAttributes};
 
@@ -75,63 +76,10 @@ function initOptionsFromAttributes(element, options, mapping = {}, prefix = 'dat
             }
 
             finder.setVia(optionName, value);
-
-            // if element has an attribute observer, then register the attribute observer
-            if (element?.[attributeObserverSymbol]) {
-                element[attributeObserverSymbol][name] = (newValue, oldValue) => {
-
-                    if (newValue === oldValue) return;
-
-                    let changedValue = newValue;
-
-                    if (typeOfOptionValue === 'boolean') {
-                        changedValue = changedValue === 'true';
-                    } else if (typeOfOptionValue === 'number') {
-                        changedValue = Number(changedValue);
-                    } else if (typeOfOptionValue === 'string') {
-                        changedValue = String(changedValue);
-                    } else if (typeOfOptionValue === 'object') {
-                        changedValue = JSON.parse(changedValue);
-                    }
-
-                    finder.setVia(optionName, changedValue);
-                }
-            }
-
-
         }
     })
 
     return options;
 }
 
-/**
- * Extracts the keys from the given object and returns a map with the keys and values.
- *
- * @private
- * @param {object} obj
- * @param {string} keyPrefix
- * @param {string} keySeparator
- * @param {string} valueSeparator
- * @returns {Map<any, any>}
- */
-function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
-    const resultMap = new Map();
 
-    function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
-        for (const key in currentObj) {
-            if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
-                const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
-                const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
-                helper(currentObj[key], newKeyPrefix, newValuePrefix);
-            } else {
-                const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
-                const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
-                resultMap.set(finalKey, finalValue);
-            }
-        }
-    }
-
-    helper(obj, keyPrefix, keyPrefix);
-    return resultMap;
-}
diff --git a/application/source/dom/util/set-option-from-attribute.mjs b/application/source/dom/util/set-option-from-attribute.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..fe39a793697d41f8f528639583e6cba9ed28c32c
--- /dev/null
+++ b/application/source/dom/util/set-option-from-attribute.mjs
@@ -0,0 +1,83 @@
+/**
+ * Copyright schukai GmbH and contributors 2023. All Rights Reserved.
+ * Node module: @schukai/monster
+ * This file is licensed under the AGPLv3 License.
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
+ */
+
+import {Pathfinder} from '../../data/pathfinder.mjs';
+import {isFunction} from '../../types/is.mjs';
+import {attributeObserverSymbol} from "../customelement.mjs";
+import {extractKeys} from "./extract-keys.mjs";
+
+export {setOptionFromAttribute};
+
+/**
+ * Set the given options object based on the attributes of the current DOM element.
+ * The function looks for attributes with the prefix 'data-monster-option-', and maps them to
+ * properties in the options object. It replaces the dashes with dots to form the property path.
+ * For example, the attribute 'data-monster-option-url' maps to the 'url' property in the options object.
+ *
+ * With the mapping parameter, the attribute value can be mapped to a different value.
+ * For example, the attribute 'data-monster-option-foo' maps to the 'bar' property in the options object.
+ *
+ * The mapping object would look like this:
+ * {
+ *    'foo': (value) => value + 'bar'
+ *    // the value of the attribute 'data-monster-option-foo' is appended with 'bar'
+ *    // and assigned to the 'bar' property in the options object.
+ *    // e.g. <div data-monster-option-foo="foo"></div>
+ *    'bar.baz': (value) => value + 'bar'
+ *    // the value of the attribute 'data-monster-option-bar-baz' is appended with 'bar'
+ *    // and assigned to the 'bar.baz' property in the options object.  
+ *    // e.g. <div data-monster-option-bar-baz="foo"></div>
+ * }
+ *
+ * @since 3.45.0
+ * @param {HTMLElement} element - The DOM element to be used as the source of the attributes.
+ * @param {Object} name - The attribute object to be used as the source of the attributes.
+ * @param {Object} options - The options object to be initialized.
+ * @param {Object} mapping - A mapping between the attribute value and the property value.
+ * @param {string} prefix - The prefix of the attributes to be considered.
+ * @returns {Object} - The initialized options object.
+ * @this HTMLElement - The context of the DOM element.
+ */
+function setOptionFromAttribute(element, name, options, mapping = {}, prefix = 'data-monster-option-') {
+    if (!(element instanceof HTMLElement)) return options;
+    if (!element.hasAttributes()) return options;
+    
+    const keyMap = extractKeys(options);
+    const finder = new Pathfinder(options);
+
+    // check if the attribute name is a valid option. 
+    // the mapping between the attribute is simple. The dash is replaced by a dot.
+    // e.g. data-monster-url => url
+    const optionName = keyMap.get(name.substring(prefix.length).toLowerCase());
+    if (!finder.exists(optionName)) return;
+
+    if (!element.hasAttribute(name)) {
+        return options;
+    }
+
+    let value = element.getAttribute(name);
+    if (mapping.hasOwnProperty(optionName) && isFunction(mapping[optionName])) {
+        value = mapping[optionName](value);
+    }
+
+    const typeOfOptionValue = typeof finder.getVia(optionName);
+    if (typeOfOptionValue === 'boolean') {
+        value = value === 'true';
+    } else if (typeOfOptionValue === 'number') {
+        value = Number(value);
+    } else if (typeOfOptionValue === 'string') {
+        value = String(value);
+    } else if (typeOfOptionValue === 'object') {
+        value = JSON.parse(value);
+    }
+
+    finder.setVia(optionName, value);
+
+    return options;
+}
+
+
diff --git a/development/playground/dom-arguments/index.html b/development/playground/dom-arguments/index.html
index 538ebea6df6c2ddd777c2c8eaa1359b5f4aa05e1..0f02359c4e18aa7f994549cf597204241eaf6506 100644
--- a/development/playground/dom-arguments/index.html
+++ b/development/playground/dom-arguments/index.html
@@ -22,8 +22,9 @@
         }
     }
 </script>
-
-<monster-1>Monster1</monster-1>
+<form>
+<monster-1 data-monster-option-a-b-c="114">Monster1</monster-1>
+</form>
 </main>
 </body>
 </html>
diff --git a/development/playground/dom-arguments/main.mjs b/development/playground/dom-arguments/main.mjs
index 70268e2ea2ae04adb513c7ad116fe2845a1a1119..05c646b3284f0323de408c0347c20b762e9e1779 100644
--- a/development/playground/dom-arguments/main.mjs
+++ b/development/playground/dom-arguments/main.mjs
@@ -2,7 +2,7 @@
 // import {Updater} from '../../../application/source/dom/updater.mjs';
 import {
     attributeObserverSymbol,
-    CustomElement,
+    CustomElement,CustomControl, 
     registerCustomElement
 } from '../../../application/source/monster.mjs';
 import {domReady} from '../../../application/source/dom/ready.mjs';
@@ -12,7 +12,7 @@ import {domReady} from '../../../application/source/dom/ready.mjs';
 
 const initMethodSymbol = Symbol.for("@schukai/monster/dom/@@initMethodSymbol");
 
-class Monster1 extends CustomElement {
+class Monster1 extends CustomControl {
 
     constructor() {
         super();