diff --git a/workbook.html b/workbook.html
index ebbfc3e2..6c32cca1 100644
--- a/workbook.html
+++ b/workbook.html
@@ -43,4 +43,9 @@ base_url: ../
handling cookies
+wui/
+
+
diff --git a/workbook/handling_request/form.md b/workbook/handling_request/form.md
index 4d762c71..13e88eb7 100644
--- a/workbook/handling_request/form.md
+++ b/workbook/handling_request/form.md
@@ -25,7 +25,7 @@ Nav: [Workbook](../../workbook) :: [Basic Concepts](../../basics/basics) :: [Han
An HTML Form can handle `GET` and `POST` requests.
When we use a form with method `GET`, the data is attached at the end of the url for example:
->http://wwww.example.com?key1=value1&...keyn=valuen
+>http://wwww.example.com/?key1=value1&...keyn=valuen
If we use the method `POST`, the data is sent to the server in a different line.
diff --git a/workbook/workbook.md b/workbook/workbook.md
index 7466df34..b8ca48fe 100644
--- a/workbook/workbook.md
+++ b/workbook/workbook.md
@@ -12,6 +12,7 @@ base_url: ../../
* [Handling Requests: Header Fields](#header_fields)
* [Generating Responses](#generating_responses)
* [Handling Cookies](#handling_cookies)
+* [Web User Interface](#wui)
* [EWF Deployment](#deployment)
@@ -44,6 +45,11 @@ Before reading (or walking throught) the workbook, to get a quick overview of EW
## Generating Response
[Generating Responses](../generating_response/generating_response)
+
+
+## Web User Interface
+[Web User Interface](../wui/readme)
+
## Handling Cookies
diff --git a/workbook/wui/readme.md b/workbook/wui/readme.md
new file mode 100644
index 00000000..52279321
--- /dev/null
+++ b/workbook/wui/readme.md
@@ -0,0 +1,240 @@
+---
+layout: default
+title: readme
+base_url: ../../../
+---
+# Web User Interface (WUI)
+
+When it comes to web user interface, HTML, CSS and Javascript are part of the solution.
+To generate the HTML and CSS, there are many solution, among them:
+* use template (such as can use templates like the [Eiffel Smarty lib](https://github.com/EiffelSoftware/EiffelStudio/tree/master/Src/contrib/library/text/template/smarty)
+* generate the html from the Eiffel code directly
+* use high level components that generate the html themselves.
+
+Within the EiffelWeb framework, the `wsf_html` library provides classes to build a structured representation of html and then generate expected HTML.
+
+
+## Overview
+The [`wsf_html` library](https://github.com/EiffelWebFramework/EWF/tree/master/library/server/wsf_html) provides:
+
+* `CSS_*` classes to generate CSS style.
+* `WSF_WIDGET_*` classes representing HTML widget (such as `HTML Response")
+ response.send (mesg)
+ end
+```
+
+In this case, the html is hardcoded, and written manually.
+Now let's see how to use the WSF widgets on a more advanced usecase, a simple sign-in web form with 2 input fields `username`, `password` and `submit` button.
+
+```eiffel
+
+ execute
+ do
+ -- Basic url dispatching...
+ if request.is_post_request_method then
+ execute_web_form_submission
+ else
+ execute_web_form_display
+ end
+ end
+
+ execute_web_form_display
+ local
+ mesg: WSF_HTML_PAGE_RESPONSE
+ f: WSF_FORM
+ l_html: STRING
+ do
+ -- Build the web form
+ f := new_login_form
+
+ -- Html generation
+ --| the first argument of `to_html` is the theme for advanced usage you can provide a custom theme
+ --| that can redefine how to generate link for instance.
+ l_html := f.to_html (create {WSF_REQUEST_THEME}.make_with_request (request))
+
+ -- Send the response
+ create mesg.make
+ mesg.set_title ("This is a Web form")
+ mesg.set_body (l_html)
+ response.send (mesg)
+ end
+
+ execute_web_form_submission
+ do
+ ... this will be explain in next section !
+ end
+
+ new_login_form: WSF_FORM
+ local
+ f: WSF_FORM
+ f_set: WSF_FORM_FIELD_SET
+ f_username: WSF_FORM_TEXT_INPUT
+ f_password: WSF_FORM_PASSWORD_INPUT
+ f_submit: WSF_FORM_SUBMIT_INPUT
+ do
+ -- Build the web form
+ create f.make ("/form", "form-login") -- The form id is optional.
+ f.set_method_post -- it could also use the default GET method.
+
+ -- Put specific html code
+ f.extend_html_text ("Web form example
")
+
+ -- username input field
+ create f_username.make ("username")
+ f_username.set_label ("User name")
+ f.extend (f_username)
+
+ -- password input field
+ create f_password.make ("password")
+ f_password.set_label ("Password")
+ f.extend (f_password)
+
+ -- submit button
+ create f_submit.make_with_text ("op", "Login")
+ f.extend (f_submit)
+
+ Result := f
+ end
+```
+
+## Handling web form data
+
+When a form is submitted, the code can access the request data thanks to the `request: WSF_REQUEST` attribute.
+See [Handling requests section](../../handling_request/form)
+
+The `wsf_html` library, thanks to the `WSF_FORM`, provides a more powerful solution.
+Indeed `WSF_HTML.process (request, .., ..)` analyze the request, fill the form fields, and process various validations, and submissions automatically.
+see
+```eiffel
+ process (req: WSF_REQUEST; a_before_callback, a_after_callback: detachable PROCEDURE [WSF_FORM_DATA])
+ -- Process Current form with request `req`,
+ -- and build `last_data: WSF_FORM_DATA` object containing the field values.
+ -- agent `a_before_callback` is called before the validation
+ -- agent `a_after_callback` is called after the validation
+```
+
+In our previous sample code, `execute_web_form_submission` could be:
+```eiffel
+ execute_web_form_submission
+ local
+ mesg: WSF_HTML_PAGE_RESPONSE
+ l_html: STRING
+ f: WSF_FORM
+ do
+ create mesg.make
+ mesg.set_title ("Web form submission")
+ create l_html.make_empty
+
+ -- Build again the same form.
+ f := new_login_form
+
+ -- Process this form with `request` data.
+ f.process (request, Void, Void)
+ if attached f.last_data as form_data and then not form_data.has_error then
+ -- `form_data` contains the submitted fields with their value.
+
+ -- Depending on the form data, display a response.
+ l_html.append ("Username: ")
+ if attached form_data.string_item ("username") as l_username then
+ -- The username may contain unicode, or characters like '<'
+ -- thus, it needs to be html encoded
+ l_html.append (mesg.html_encoded_string (l_username))
+ else
+ l_html.append ("missing value !!")
+ end
+ l_html.append ("
")
+ if attached form_data.string_item ("password") as l_password then
+ l_html.append ("Password: ")
+ -- The password may contain unicode, or characters like '<'
+ -- thus, it needs to be html encoded
+ l_html.append (mesg.html_encoded_string (l_password))
+ else
+ l_html.append ("missing value !!")
+ end
+ l_html.append ("
")
+ else
+ l_html.append ("Error while processing the web form!")
+
+ -- Display again the form (it will contain the submitted values, if any).
+ f.append_to_html (create {WSF_REQUEST_THEME}.make_with_request (request), l_html)
+ end
+
+ mesg.set_body (l_html)
+ response.send (mesg)
+ end
+```
+
+In this case, the code could report an error if the username is empty, or with unwanted character ... this could be done in the `execute_web_form_submission` code, but it is also possible to set validation on each form field.
+If those validations reports error, then the `form_data.has_error` will be True.
+
+To set validation,
+For instance, in previous sample, accepts only alpha-numeric characters:
+```eiffel
+ f_username.set_description ("Only alpha-numeric character are accepted.")
+ f_username.set_validation_action (agent (fd: WSF_FORM_DATA)
+ do
+ if attached fd.string_item ("username") as u then
+ if across u as ic some not ic.item.is_alpha_numeric end then
+ fd.report_invalid_field ("username", "Missing username value!")
+ elseif u.is_whitespace then
+ fd.report_invalid_field ("username", "Empty username value!")
+ end
+ else
+ fd.report_invalid_field ("username", "Missing username value!")
+ end
+ end)
+```
+Notes:
+* If the validation is not satisfied, the form data will report `has_error` as True, and the associated form will be updated with submitted values, and with `class="error"` set to the related html code.
+The errors are also available via the `form_data.errors` feature.
+* Since the validation feature argument is the `WSF_FORM_DATA` itself, it is also possible to validate several fields at once.
+
+If there is no error, the form submission process will trigger the `WSF_FORM.submit_actions`. This could be used when the form is built by different components, and each component will handle the submission of its associated fields.
+
+
+## Catalog of widgets
+The `wsf_html` library includes a collection of widgets and form items:
+
+### `WSF_WIDGET_...`
+* `.._DIV`: `..` html element.
+* `.._IMAGE`: `
` html element.
+* `.._RAW_TEXT`: html escaped text.
+* `.._TEXT`: html text.
+And more advanced widgets such as:
+* `.._PAGER`: to generate pager widget like ` << 1 2 3 .. >> `
+* `.._TABLE`: a set of classes to generate `` html element.
+
+
+### `WSF_FORM_...`
+Widget usually included in web forms, such as
+* `WSF_FORM_*_INPUT`: text, password, file, submit, ...
+* `WSF_FORM_FIELD_SET`, `.._TEXTAREA` ...
+
+
+## What about CSS style
+The `CSS_STYLE`, `CSS_SELECTOR` and `CSS_TEXT` can be used to generate CSS style sheat, but they can also be used to set css style to `WSF_WIDGET`.
+
+
+## Potential future evolutions
+For now, the `wsf_html` provides a simple solution to build web form. It is always possible to add new `WSF_WIDGET` to support more html elements.
+Advanced usage could be built on top of the `wsf_html` to include for instance javascript actions, but this is out of the scope of `wsf_html` .
+
+
+