-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source. For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code. There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d8be964
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+all: build
+
+serve:
+ hugo server --buildDrafts --disableFastRender
+
+build:
+ hugo build
diff --git a/README.md b/README.md
deleted file mode 100644
index 3754083..0000000
--- a/README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# go-form
-
-Creating and processing HTML forms is hard and repetitive. You need to deal with rendering HTML form fields, validating submitted data, mapping the form data into objects and a lot more. [`go-form`][go-form] includes a powerful form feature that provides all these features.
-
-[`go-form`][go-form] is heavily influenced by [Symfony Form](https://symfony.com/doc/current/forms.html). It includes:
-
-* A form builder based on fields declarations and independent of structs
-* Validation based on constraints
-* Data mounting to populate a form from a struct instance
-* Data binding to populate a struct instance from a submitted form
-* Form renderer with customizable themes
-
-## Documentation
-
-* [Official documentation][doc]
-* [Demo][demo]
-
-[go-form]: https://gitnet.fr/deblan/go-form
-[demo]: https://gitnet.fr/deblan/go-form-demo
-[doc]: https://deblan.gitnet.page/go-form/
diff --git a/archetypes/default.md b/archetypes/default.md
new file mode 100644
index 0000000..0d5eebd
--- /dev/null
+++ b/archetypes/default.md
@@ -0,0 +1,5 @@
+---
+date: '{{ .Date }}'
+draft: true
+title: '{{ replace .File.ContentBaseName "-" " " | title }}'
+---
diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..202c701
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,60 @@
+.hidden {
+ display: none;
+}
+
+.hugo-goplay-summary {
+ cursor: pointer;
+}
+
+.hugo-goplay-textarea {
+ padding: 1rem;
+ width: 100%;
+ border-radius: 4px;
+ margin: 10px 0;
+ background-color: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27) / 0.1);
+ font-family: monospace;
+}
+
+.hugo-goplay-result pre {
+ padding: 1rem;
+}
+
+.hugo-goplay-result code {
+ background: none !important;
+ border: 0 !important;
+}
+
+.hugo-goplay-result .system {
+ color: green;
+}
+
+.hugo-goplay-result .stderr {
+ color: red;
+}
+
+.hugo-goplay-toolbox {
+ display: flex;
+ justify-content: flex-start;
+ margin-bottom: 2rem;
+}
+
+.hugo-goplay-toolbox .hugo-goplay-button {
+ font-size: 13px;
+ font-weight: bold;
+ border: 1px solid var(--primary);
+ padding: 0.15rem 0.75rem;
+ border-radius: 4px;
+ margin-left: 0.5rem;
+ color: var(--primary);
+ background-color: var(--theme);
+}
+
+.hugo-goplay-toolbox .hugo-goplay-button:hover {
+ border: 1px solid var(--theme);
+ color: var(--theme);
+ background-color: var(--primary);
+}
+
+.hugo-goplay-toolbox .hugo-goplay-button:first-child {
+ margin-left: 0;
+}
diff --git a/content/_index.md b/content/_index.md
new file mode 100644
index 0000000..c96bcf4
--- /dev/null
+++ b/content/_index.md
@@ -0,0 +1,58 @@
+---
+layout: hextra-home
+title: "Welcome!"
+---
+
+{{< hextra/hero-badge >}}
+
+ Free, open source
+ {{< icon name="arrow-circle-right" attributes="height=14" >}}
+{{< /hextra/hero-badge >}}
+
+
+{{< hextra/hero-headline >}}
+ Create and process forms with Golang!
+{{< /hextra/hero-headline >}}
+
+
+
+{{< hextra/hero-subtitle >}}
+ Fast, batteries-included Hugo theme
for creating beautiful static websites
+{{< /hextra/hero-subtitle >}}
+
+
+
+
+{{< hextra/hero-button text="Get Started" link="docs" >}}
+
+
+
+
+
+
+{{< hextra/feature-grid >}}
+ {{< hextra/feature-card
+ title="Fast and Full-featured"
+ subtitle="Simple and easy to use, yet powerful and feature-rich."
+ class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
+ image="images/hextra-doc.webp"
+ imageClass="hx:top-[40%] hx:left-[24px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
+ style="background: radial-gradient(ellipse at 50% 80%,rgba(194,97,254,0.15),hsla(0,0%,100%,0));"
+ >}}
+ {{< hextra/feature-card
+ title="Form builder and validation"
+ subtitle="Compose with just Markdown. Enrich with Shortcode components."
+ class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-lg:min-h-[340px]"
+ image="images/hextra-markdown.webp"
+ imageClass="hx:top-[40%] hx:left-[36px] hx:w-[180%] hx:sm:w-[110%] hx:dark:opacity-80"
+ style="background: radial-gradient(ellipse at 50% 80%,rgba(142,53,74,0.15),hsla(0,0%,100%,0));"
+ >}}
+ {{< hextra/feature-card
+ title="Mount and bind any struct"
+ subtitle="Built-in full text search with FlexSearch, no extra setup required."
+ class="hx:aspect-auto hx:md:aspect-[1.1/1] hx:max-md:min-h-[340px]"
+ image="images/hextra-search.webp"
+ imageClass="hx:top-[40%] hx:left-[36px] hx:w-[110%] hx:sm:w-[110%] hx:dark:opacity-80"
+ style="background: radial-gradient(ellipse at 50% 80%,rgba(221,210,59,0.15),hsla(0,0%,100%,0));"
+ >}}
+{{< /hextra/feature-grid >}}
diff --git a/content/docs/Installation.md b/content/docs/Installation.md
new file mode 100644
index 0000000..cb641b4
--- /dev/null
+++ b/content/docs/Installation.md
@@ -0,0 +1,9 @@
+---
+linkTitle: Installation
+title: Installation
+weight: 2
+---
+
+```golang
+go get gitnet.fr/deblan/go-form
+```
diff --git a/content/docs/_index.md b/content/docs/_index.md
new file mode 100644
index 0000000..4a7b3d7
--- /dev/null
+++ b/content/docs/_index.md
@@ -0,0 +1,14 @@
+---
+linkTitle: "Documentation"
+title: Introduction
+---
+
+Creating and processing HTML forms is hard and repetitive. You need to deal with rendering HTML form fields, validating submitted data, mapping the form data into objects and a lot more. **go-form** includes a powerful form feature that provides all these features.
+
+**go-form** is heavily influenced by [Symfony Form](https://symfony.com/doc/current/forms.html). It includes:
+
+* A form builder based on fields declarations and independent of structs
+* Validation based on constraints
+* Data mounting to populate a form from a struct instance
+* Data binding to populate a struct instance from a submitted form
+* Form renderer with customizable themes
diff --git a/content/docs/constraints/_index.md b/content/docs/constraints/_index.md
new file mode 100644
index 0000000..d874e08
--- /dev/null
+++ b/content/docs/constraints/_index.md
@@ -0,0 +1,153 @@
+---
+linkTitle: Constraints
+title: Constraints
+weight: 5
+---
+
+The validation is designed to validate data against constraints.
+
+## Import
+
+```golang
+import (
+ "gitnet.fr/deblan/go-form/validation"
+)
+```
+
+## Constraints
+
+### Length
+
+Validate the length of an array, a slice or a string
+
+```golang
+c := validation.NewLength()
+
+// Define minimum
+c.WithMin(1)
+
+// Define minimum
+c.WithMax(100)
+
+// Define min and max
+// Equivalent to c.WithMin(50).WithMax(50)
+c.WithExact(50)
+```
+
+### Mail
+
+```golang
+validation.NewMail()
+```
+
+### Not blank
+
+```golang
+validation.NewNotBlank()
+```
+
+### Range
+
+Validate a number
+
+```golang
+c := validation.NewRange()
+
+// Define minimum
+c.WithMin(1)
+
+// Define minimum
+c.WithMax(100)
+
+// Define min and max
+// Equivalent to c.WithMin(1).WithMax(100)
+c.WithRange(1, 100)
+```
+
+### Regex
+
+Validate a string with a regex
+
+```golang
+c := validation.NewRegex(`expression`)
+
+// The value must match
+c.MustMatch()
+
+// The value must not match
+c.MustNotMatch()
+```
+
+### Is even
+
+Validate that a number is even.
+
+```golang
+validation.NewIsEven()
+```
+
+### Is odd
+
+Validate that a number is odd.
+
+```golang
+validation.NewIsOdd()
+```
+
+
+## Custom constraint
+
+Use case: you want to validate that the data equals "example"
+
+```golang
+package validation
+
+import (
+ "reflect"
+
+ v "gitnet.fr/deblan/go-form/validation"
+)
+
+// Define a struct
+type IsExample struct {
+ Message string
+ TypeErrorMessage string
+}
+
+// Create a factory
+func NewIsExample() IsEven {
+ return IsEven{
+ Message: "This value does not equal \"example\".",
+ TypeErrorMessage: "This value can not be processed.",
+ }
+}
+
+// Implement the validation
+func (c IsExample) Validate(data any) []Error {
+ errors := []Error{}
+
+ // Should not validate blank data
+ if len(v.NewNotBlank().Validate(data)) != 0 {
+ return []Error{}
+ }
+
+ t := reflect.TypeOf(data)
+
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ switch t.Kind() {
+ case reflect.String:
+ if data.(string) != "example" {
+ errors = append(errors, Error(c.Message))
+ }
+
+ default:
+ errors = append(errors, Error(c.TypeErrorMessage))
+ }
+
+ return errors
+}
+```
+
diff --git a/content/docs/fields/_index.md b/content/docs/fields/_index.md
new file mode 100644
index 0000000..63fb9be
--- /dev/null
+++ b/content/docs/fields/_index.md
@@ -0,0 +1,384 @@
+---
+linkTitle: Fields
+title: Fields
+weight: 4
+---
+
+A field represents a field in a form.
+
+### Checkbox
+
+```golang
+func NewFieldCheckbox(name string) *Field
+```
+
+{{% goplay-field %}}
+field := form.NewFieldCheckbox("Foo")
+{{% /goplay-field %}}
+
+Generates an input[type=checkbox]
+
+### Choice
+
+```golang
+func NewFieldChoice(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldChoice("Foo")
+
+{{% /goplay-field %}}
+
+
+Generates inputs (checkbox or radio) or selects
+
+### Csrf
+
+```golang
+func NewFieldCsrf(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldCsrf("Foo")
+
+{{% /goplay-field %}}
+
+### Date
+
+```golang
+func NewFieldDate(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldDate("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=date] with default transformers
+
+### Datetime
+
+```golang
+func NewFieldDatetime(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldDatetime("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=datetime] with default transformers
+
+### DatetimeLocal
+
+```golang
+func NewFieldDatetimeLocal(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldDatetimeLocal("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=datetime-local] with default transformers
+
+### Hidden
+
+```golang
+func NewFieldHidden(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldHidden("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=hidden]
+
+### Mail
+
+```golang
+func NewFieldMail(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldMail("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=email]
+
+### Number
+
+```golang
+func NewFieldNumber(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldNumber("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=number] with default transformers
+
+### Password
+
+```golang
+func NewFieldPassword(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldPassword("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=password]
+
+### Range
+
+```golang
+func NewFieldRange(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldRange("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=range]
+
+### Sub Form
+
+```golang
+func NewFieldSubForm(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldSubForm("Foo")
+
+{{% /goplay-field %}}
+
+Alias:
+
+```golang
+func NewSubForm(name string) *Field
+```
+
+Generates a sub form
+
+### Text
+
+```golang
+func NewFieldText(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldText("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=text]
+
+### Textarea
+
+```golang
+func NewFieldTextarea(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldTextarea("Foo")
+
+{{% /goplay-field %}}
+
+Generates a textarea
+
+### Time
+
+```golang
+func NewFieldTime(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewFieldTime("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=time] with default transformers
+
+### Submit
+
+```golang
+func NewSubmit(name string) *Field
+```
+
+{{% goplay-field %}}
+
+field := form.NewSubmit("Foo")
+
+{{% /goplay-field %}}
+
+Generates an input[type=submit]
+
+## Methods
+
+### Add
+
+```golang
+func (f *Field) Add(children ...*Field) *Field
+```
+
+Appends children
+
+### Bind
+
+```golang
+func (f *Field) Bind(data map[string]any, key *string) error
+```
+
+Bind the data into the given map
+
+### GetChild
+
+```golang
+func (f *Field) GetChild(name string) *Field
+```
+
+Returns a child using its name
+
+### GetId
+
+```golang
+func (f *Field) GetId() string
+```
+
+Computes the id of the field
+
+### GetName
+
+```golang
+func (f *Field) GetName() string
+```
+
+Computes the name of the field
+
+### GetOption
+
+```golang
+func (f *Field) GetOption(name string) *Option
+```
+
+Returns an option using its name
+
+### HasChild
+
+```golang
+func (f *Field) HasChild(name string) bool
+```
+
+Checks if the field contains a child using its name
+
+### HasOption
+
+```golang
+func (f *Field) HasOption(name string) bool
+```
+
+Checks if the field contains an option using its name
+
+### Mount
+
+```golang
+func (f *Field) Mount(data any) error
+```
+
+Populates the field with data
+
+### ResetErrors
+
+```golang
+func (f *Field) ResetErrors() *Field
+```
+
+Resets the field errors
+
+### WithBeforeBind
+
+```golang
+func (f *Field) WithBeforeBind(callback func(data any) (any, error)) *Field
+```
+
+Sets a transformer applied to the data of a field before defining it in a structure
+
+### WithBeforeMount
+
+```golang
+func (f *Field) WithBeforeMount(callback func(data any) (any, error)) *Field
+```
+
+Sets a transformer applied to the structure data before displaying it in a field
+
+### WithConstraints
+
+```golang
+func (f *Field) WithConstraints(constraints ...validation.Constraint) *Field
+```
+
+Appends constraints
+
+### WithData
+
+```golang
+func (f *Field) WithData(data any) *Field
+```
+
+Sets data the field
+
+### WithFixedName
+
+```golang
+func (f *Field) WithFixedName() *Field
+```
+
+Sets that the name of the field is not computed
+
+### WithOptions
+
+```golang
+func (f *Field) WithOptions(options ...*Option) *Field
+```
+
+#### Common options
+
+| Name | type | description | Info |
+| ---- | ---- | ---- | ---- |
+| `required` | `bool` | Add `required="true"` | Does not apply a constraint |
+| `attr` | `form.Attrs` | List of extra attributes of the field | |
+| `row_attr` | `form.Attrs` | List of extra attributes of the field's top container | |
+| `label` | `string` | The label of the field | Usually show before the field |
+| `label_attr` | `form.Attrs` | List of extra attributes of the label | |
+| `help` | `string` | Helper of the field | |
+| `help_attr` | `form.Attrs` | List of extra attributes of the help | |
+
+Appends options to the field
+
+### WithSlice
+
+```golang
+func (f *Field) WithSlice() *Field
+```
+
+Sets that the field represents a data slice
diff --git a/content/docs/form/_index.md b/content/docs/form/_index.md
new file mode 100644
index 0000000..60466be
--- /dev/null
+++ b/content/docs/form/_index.md
@@ -0,0 +1,243 @@
+---
+linkTitle: Form
+title: Form
+weight: 3
+---
+
+## Example
+
+### Prerequisites
+
+```golang
+import (
+ "gitnet.fr/deblan/go-form/form"
+ "gitnet.fr/deblan/go-form/validation"
+)
+
+type Person struct {
+ Name string
+ Age int
+}
+```
+
+### Creating a form
+
+```golang
+myForm := form.NewForm(
+ form.NewFieldText("Name").
+ WithConstraints(
+ validation.NewNotBlank(),
+ ),
+ form.NewFieldNumber("Age").
+ WithConstraints(
+ validation.NewNotBlank(),
+ validation.NewRange().WithMin(18),
+ ),
+).End()
+```
+
+### Validating a struct
+
+```golang
+data := Person{}
+
+myForm.Mount(data)
+myForm.IsValid() // false
+
+data = Person{
+ Name: "Alice",
+ Age: 42,
+}
+
+myForm.Mount(data)
+myForm.IsValid() // true
+```
+
+### Validating a request
+
+```golang
+import (
+ "net/http"
+)
+
+myForm.WithMethod(http.MethodPost)
+
+// req *http.Request
+if req.Method == myForm.Method {
+ myForm.HandleRequest(req)
+
+ if myForm.IsSubmitted() && myForm.IsValid() {
+ myForm.Bind(&data)
+ }
+}
+```
+
+## Struct
+
+```golang
+type Form struct {
+ Fields []*Field
+ GlobalFields []*Field
+ Errors []validation.Error
+ Method string
+ Action string
+ Name string
+ Options []*Option
+ RequestData *url.Values
+}
+```
+
+## Methods
+
+### NewForm
+
+```golang
+func NewForm(fields ...*Field) *Form
+```
+
+Generates a new form with default properties
+
+### Add
+
+```golang
+func (f *Form) Add(fields ...*Field)
+```
+
+Appends children
+
+### AddGlobalField
+
+```golang
+func (f *Form) AddGlobalField(field *Field)
+```
+
+Configures its children deeply
+
+### Bind
+
+```golang
+func (f *Form) Bind(data any) error
+```
+
+Copies datas from the form to a struct
+
+### End
+
+```golang
+func (f *Form) End() *Form
+```
+
+Configures its children deeply This function must be called after adding all
+
+fields
+
+### GetField
+
+```golang
+func (f *Form) GetField(name string) *Field
+```
+
+Returns a child using its name
+
+### GetOption
+
+```golang
+func (f *Form) GetOption(name string) *Option
+```
+
+Returns an option using its name
+
+### HandleRequest
+
+```golang
+func (f *Form) HandleRequest(req *http.Request)
+```
+
+Processes a request
+
+### HasField
+
+```golang
+func (f *Form) HasField(name string) bool
+```
+
+Checks if the form contains a child using its name
+
+### HasOption
+
+```golang
+func (f *Form) HasOption(name string) bool
+```
+
+Checks if the form contains an option using its name
+
+### IsSubmitted
+
+```golang
+func (f *Form) IsSubmitted() bool
+```
+
+Checks if the form is submitted
+
+### IsValid
+
+```golang
+func (f *Form) IsValid() bool
+```
+
+Checks the a form is valid
+
+### Mount
+
+```golang
+func (f *Form) Mount(data any) error
+```
+
+Copies datas from a struct to the form
+
+### ResetErrors
+
+```golang
+func (f *Form) ResetErrors() *Form
+```
+
+Resets the form errors
+
+### WithAction
+
+```golang
+func (f *Form) WithAction(v string) *Form
+```
+
+Sets the action of the form (eg: "/")
+
+### WithMethod
+
+```golang
+func (f *Form) WithMethod(v string) *Form
+```
+
+Sets the method of the format (http.MethodPost, http.MethodGet, ...)
+
+### WithName
+
+```golang
+func (f *Form) WithName(v string) *Form
+```
+
+Sets the name of the form (used to compute name of fields)
+
+### WithOptions
+
+```golang
+func (f *Form) WithOptions(options ...*Option) *Form
+```
+
+Appends options to the form
+
+#### Options
+
+ | Name | Type | Description |
+ | ---- | ---- | ---- |
+ | `attr` | `map[string]string` | List of extra attributes |
+ | `help` | `string` | Helper |
diff --git a/content/docs/rendering/_index.md b/content/docs/rendering/_index.md
new file mode 100644
index 0000000..4bc00d3
--- /dev/null
+++ b/content/docs/rendering/_index.md
@@ -0,0 +1,67 @@
+---
+linkTitle: Rendering
+title: Rendering
+weight: 6
+---
+
+**go-form** allows you to render a form using Go's built-in template engine.
+Here is a simple example that displays a form:
+
+```golang
+myForm := form.NewForm(...)
+
+render := theme.NewRenderer(theme.Bootstrap5)
+
+tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
+
+
+ My form
+
+
+ {{ form .Form }}
+
+
+`)
+
+b := new(strings.Builder)
+
+tpl.Execute(w, map[string]any{
+ "Form": myForm,
+})
+
+fmt.Println(b.String())
+```
+
+{{% goplay-auto-import-main %}}
+
+@import "fmt"
+@import "html/template"
+@import "strings"
+@import
+@import "github.com/yosssi/gohtml"
+@import "gitnet.fr/deblan/go-form/example"
+@import "gitnet.fr/deblan/go-form/theme"
+
+form := example.CreateDataForm()
+render := theme.NewRenderer(theme.Html5)
+// render := theme.NewRenderer(theme.Bootstrap5)
+
+tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`{{ form .Form }}`)
+
+buff := new(strings.Builder)
+
+tpl.Execute(buff, map[string]any{
+ "Form": form,
+})
+
+fmt.Println(gohtml.Format(buff.String()))
+
+{{% /goplay-auto-import-main %}}
+
+Other helper functions are available to render specific parts of the form:
+
+- `form_errors`: displays the form's global errors
+- `form_row` : renders the label, errors, and widget of a field
+- `form_label`: renders only the label of a field
+- `form_widget`: renders only the widget of a field
+- `form_widget_errors`: renders only the errors of a specific field
diff --git a/content/docs/rendering/theming.md b/content/docs/rendering/theming.md
new file mode 100644
index 0000000..252e940
--- /dev/null
+++ b/content/docs/rendering/theming.md
@@ -0,0 +1,11 @@
+---
+linkTitle: Theming
+title: Theming
+---
+
+**go-form** provides 2 themes:
+
+- `theme.Html5`: a basic view without classes
+- `theme.Bootstrap5`: a theme for [Bootstrap 5](https://getbootstrap.com/)
+
+You can add a custom theme. Learn by reading the [Bootstrap5](https://gitnet.fr/deblan/go-form/src/branch/main/theme/bootstrap5.go) theme.
diff --git a/content/docs/workflow/_index.md b/content/docs/workflow/_index.md
new file mode 100644
index 0000000..6f2b485
--- /dev/null
+++ b/content/docs/workflow/_index.md
@@ -0,0 +1,120 @@
+---
+linkTitle: Workflow
+title: Workflow
+weight: 2
+---
+
+{{% steps %}}
+
+### Import
+
+```golang
+import (
+ "html/template"
+ "net/http"
+
+ "gitnet.fr/deblan/go-form/form"
+ "gitnet.fr/deblan/go-form/theme"
+)
+```
+
+### Create a form
+
+```golang
+// Let's create a new form
+// You can pass *form.Field as arguments
+myForm := form.NewForm(field1, field2, ...)
+
+// Add somes fields
+myForm.Add(field3, field4, ...)
+
+// Set the method
+//