diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4772ad0868c379530427e4727620e70916906cf..c838aa90e65e90ce67cd9a60f958c124e503defb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added ###
 
+* `form_elements` module: New `pattern` option for form fields of `type="text"`.
+* `form_panel` new `auto_focus` parameter for the `create_show_form_callback`.
 * The file-upload module from the [CaosDB WebUI Legacy
   Adapter](https://gitlab.com/caosdb/caosdb-webui-legacy-adapter) has been
   added. The new file-upload add a "+" button to the file-system view for
diff --git a/src/core/js/form_elements.js b/src/core/js/form_elements.js
index 5099971e402ddf3f9bb94580fad737c5b30594e9..63c76807a3d0462a2b6701dc47c2d83c72a134fc 100644
--- a/src/core/js/form_elements.js
+++ b/src/core/js/form_elements.js
@@ -1337,14 +1337,26 @@ var form_elements = new function () {
         return this._make_input(config);
     }
 
+    /**
+     * Field config for a text field (config.type = 'text').
+     *
+     * @augments {FieldConfig}
+     *
+     * @property {string} pattern - a regex pattern which validates the value of the text field.
+     */
+
     /**
      * Return a new text field.
      *
-     * @param {FieldConfig} config
+     * @param {TextFieldConfig} config
      * @return {HTMLElement}
      */
     this.make_text_input = function (config) {
-        return this._make_input(config);
+        const input = this._make_input(config);
+        if (config.pattern) {
+            $(input).find("input").attr("pattern", config.pattern);
+        }
+        return input;
     }
 
 
@@ -1528,6 +1540,19 @@ var form_elements = new function () {
     }
 
 
+    /**
+     * Check form validity and return if the form is valid.
+     *
+     * @param {HTMLElement} form - the form elemeng.
+     * @return {boolean} true iff the form is valid.
+     */
+    this.check_form_validity = function (form) {
+        const is_valid = form.checkValidity();
+        form.reportValidity();
+        return is_valid;
+    }
+
+
     this.all_required_fields_set = function (form) {
         const req = form_elements.get_enabled_required_fields(form);
         for (const field of req) {
@@ -1542,7 +1567,7 @@ var form_elements = new function () {
      * @param {HTMLElement} form - the form be validated.
      */
     this.is_valid = function (form) {
-        return form_elements.all_required_fields_set(form);
+        return form_elements.all_required_fields_set(form) && form_elements.check_form_validity(form);
     }
 
 
diff --git a/src/core/js/form_panel.js b/src/core/js/form_panel.js
index a745f949f98d6219e377783d7ca6c854908df573..fc1cbc7d947833f83c1ef3c39aff19117f76cc27 100644
--- a/src/core/js/form_panel.js
+++ b/src/core/js/form_panel.js
@@ -71,9 +71,12 @@ var form_panel = new function () {
      * You may supply 'undefined' as form_config and use form_creator
      * instead which is supposed to be a function without argument that
      * return the form to be added to the panel
+     *
+     * The first input element will be focused unless you specify `false` as
+     * fifths parameter.
      */
     this.create_show_form_callback = function (
-        panel_id, title, form_config, form_creator=undefined
+        panel_id, title, form_config, form_creator=undefined, auto_focus=true
     ) {
         return (e) => {
             logger.trace("enter show_form_panel", e);
@@ -100,6 +103,11 @@ var form_panel = new function () {
                     true
                 );
             }
+            if (typeof auto_focus === "undefined" || !!auto_focus === true) {
+                if (panel.find("form")[0].length>0) {
+                    panel.find("form")[0][0].focus();
+                }
+            }
         }
     };
 
diff --git a/test/core/js/modules/form_elements.js.js b/test/core/js/modules/form_elements.js.js
index f93fde0db2d69156312b2a34c3748112619b68eb..c1fa09ddbca3708d82a68c8ec4351c7951e81b16 100644
--- a/test/core/js/modules/form_elements.js.js
+++ b/test/core/js/modules/form_elements.js.js
@@ -786,3 +786,24 @@ QUnit.test("make_file_input", function (assert) {
     assert.ok(file_input.find(":input").prop("multiple"), "is multiple");
     assert.equal(file_input.find(":input").attr("accept"), ".tsv, .csv", "accept there");
 });
+
+QUnit.test("pattern", function (assert) {
+    const config = {
+        "name": "test-form",
+        "fields": [{
+            "required": true,
+            "name": "text_field",
+            "type": "text",
+            "pattern": "[a-f]*"
+        }, ],
+    }
+    const form_wrapper = $(form_elements.make_form(config));
+    const form = form_wrapper.find("form")[0];
+
+    assert.notOk(form_elements.check_form_validity(form), "empty -> invalid");
+    form_wrapper.find(":input[name='text_field']").val("ghi")
+    assert.notOk(form_elements.check_form_validity(form), "ghi -> invalid");
+
+    form_wrapper.find(":input[name='text_field']").val("abc")
+    assert.ok(form_elements.check_form_validity(form), "abc -> valid");
+});
diff --git a/test/core/js/modules/form_panel.js.js b/test/core/js/modules/form_panel.js.js
index a810fb8711f14bc3b3637297c9f937884022fc15..0203e0b50a1f11bca653ffb8ff6e8b36824b73e3 100644
--- a/test/core/js/modules/form_panel.js.js
+++ b/test/core/js/modules/form_panel.js.js
@@ -67,6 +67,16 @@ QUnit.test("create_show_form_callback ", function (assert) {
     );
     assert.equal(typeof cb2, "function", "function created");
     cb2()
+    assert.notOk(document.querySelector(`#${panel_id}`), "panel is being appended to nav, but there is no nav");
+    document.body.appendChild(document.createElement("nav"));
+
+    cb2()
+    assert.ok(document.querySelector(`#${panel_id}`), "panel was appended to nav");
+    console.log(document.activeElement, "here");
+    assert.equal(document.activeElement.name, "csv_file", "first form field is being focussed");
+
+    document.querySelector(`#${panel_id} form`).dispatchEvent(form_elements.cancel_form_event);
+    assert.notOk(document.querySelector(`#${panel_id}`), "query panel removed");
 });