diff --git a/docs/classic-ui/templates.md b/docs/classic-ui/templates.md
index b9c012ab6..166214e29 100644
--- a/docs/classic-ui/templates.md
+++ b/docs/classic-ui/templates.md
@@ -1,13 +1,974 @@
---
myst:
html_meta:
- "description": ""
- "property=og:description": ""
- "property=og:title": ""
- "keywords": ""
+ "description": "Page Templates in Plone Classic UI using TAL, TALES, and METAL"
+ "property=og:description": "Page Templates in Plone Classic UI using TAL, TALES, and METAL"
+ "property=og:title": "Templates"
+ "keywords": "Plone, Classic UI, templates, TAL, TALES, METAL, Chameleon, page templates"
---
(classic-ui-templates-label)=
# Templates
+Page Templates are the primary way to generate HTML output in Plone Classic UI.
+They are HTML files enhanced with special attributes written in TAL (Template Attribute Language), TALES (TAL Expression Syntax), and METAL (Macro Expansion for TAL).
+
+Plone uses [Chameleon](https://chameleon.readthedocs.io/) as its template engine, integrated through the Zope framework.
+Chameleon is a fast HTML/XML template engine that implements the ZPT (Zope Page Templates) specification with additional features.
+
+
+(templates-basics-label)=
+
+## Template basics
+
+A Page Template is a valid HTML or XML file with special `tal:`, `metal:`, and `i18n:` attributes that control how the template is rendered.
+
+Here is a minimal example:
+
+```html
+
+
+
+
+
Hello, World!
+
Placeholder title
+
+
+
+
+```
+
+The three parts serve different purposes:
+
+TAL (Template Attribute Language)
+: Controls the structure and content of the output.
+ TAL attributes like `tal:content`, `tal:repeat`, and `tal:condition` modify how elements are rendered.
+
+TALES (TAL Expression Syntax)
+: Defines the syntax for expressions used in TAL attributes.
+ TALES supports path expressions, Python expressions, string expressions, and more.
+
+METAL (Macro Expansion for TAL)
+: Enables template reuse through macros and slots.
+ Use METAL to inherit from base templates and define reusable template fragments.
+
+
+(templates-filesystem-vs-ttw-label)=
+
+## Filesystem vs. TTW templates
+
+Templates in Plone can be stored in two locations, with important security implications:
+
+**Filesystem templates** (recommended)
+: Templates stored in your add-on package's directory structure, typically in `browser/templates/` or `views`.
+ These templates run as **trusted code** with full Python capabilities, just like any other code in your package.
+ They have no security restrictions on Python expressions.
+
+**TTW (Through-The-Web) templates**
+: Templates created and stored in the ZODB through the Zope Management Interface (ZMI).
+ These templates run with **RestrictedPython** sandboxing for security.
+ They have limited Python functionality: no `import` statements, restricted builtins, and security-checked attribute access.
+
+```{note}
+Always develop templates on the filesystem in your add-on package.
+TTW templates are discouraged for production code because they are harder to maintain, version control, and test.
+```
+
+
+(templates-tal-label)=
+
+## TAL statements
+
+TAL uses special attributes to control template rendering.
+When an element has multiple TAL attributes, they execute in this order:
+
+1. `tal:define`
+2. `tal:condition`
+3. `tal:repeat`
+4. `tal:content` or `tal:replace`
+5. `tal:attributes`
+6. `tal:omit-tag`
+
+
+(templates-tal-define-label)=
+
+### `tal:define`
+
+Defines one or more variables for use in the template.
+
+```html
+
+
Portal URL: ${portal_url}
+
User: ${user/getId}
+
+```
+
+Use semicolons to define multiple variables.
+Variables are available within the element and its children.
+
+For global variables accessible throughout the template, use `tal:define="global varname expression"`.
+
+
+(templates-tal-condition-label)=
+
+### `tal:condition`
+
+Conditionally includes or excludes an element and its children.
+
+```html
+
+ ${context/description}
+
+
+
+ Found ${python:len(items)} items.
+
+
+
+
+ No description available.
+
+```
+
+If the condition evaluates to a false value, the entire element and all its children are removed from the output.
+
+
+(templates-tal-repeat-label)=
+
+### `tal:repeat`
+
+Repeats an element for each item in a sequence.
+
+```html
+
+
+ Item ${item}
+
+
+
+
+
+
${brain/Title}
+
${brain/Description}
+
+
+```
+
+Inside a repeat loop, you have access to the `repeat` variable which provides information about the current iteration:
+
+| Variable | Description |
+|----------|-------------|
+| `repeat/item/index` | Zero-based index (0, 1, 2, ...) |
+| `repeat/item/number` | One-based index (1, 2, 3, ...) |
+| `repeat/item/even` | True for even indices |
+| `repeat/item/odd` | True for odd indices |
+| `repeat/item/start` | True for the first item |
+| `repeat/item/end` | True for the last item |
+| `repeat/item/length` | Total number of items |
+| `repeat/item/letter` | Lowercase letter (a, b, c, ...) |
+| `repeat/item/Letter` | Uppercase letter (A, B, C, ...) |
+
+Example using repeat variables:
+
+```html
+
+
+
${repeat/item/number}
+
${item/title}
+
+
+```
+
+
+(templates-tal-content-label)=
+
+### `tal:content`
+
+Replaces the content of an element with the expression result.
+
+```html
+
Placeholder Title
+
+
+ This placeholder text will be replaced.
+
+```
+
+By default, content is HTML-escaped.
+To insert raw HTML, use the `structure` keyword:
+
+```html
+
+ Raw HTML will be inserted here.
+
+```
+
+
+(templates-tal-replace-label)=
+
+### `tal:replace`
+
+Replaces the entire element (not just its content) with the expression result.
+
+```html
+Placeholder
+
+
+
+
+```
+
+
+(templates-tal-attributes-label)=
+
+### `tal:attributes`
+
+Sets or modifies HTML attributes dynamically.
+
+```html
+
+ ${context/title}
+
+
+
+
+
+ ...
+
+```
+
+Setting an attribute to `None` removes it from the output.
+
+
+(templates-tal-omit-tag-label)=
+
+### `tal:omit-tag`
+
+Removes the element tag but keeps its content.
+
+```html
+
+ This text appears without any wrapper.
+
+
+
+
+```
+
+
+(templates-tal-block-label)=
+
+### Pure TAL blocks
+
+When you need TAL logic without generating HTML elements, use the `tal:block` style elements:
+
+```html
+
+
+
${item/Title}
+
+
+```
+
+You can use any tag name with the `tal:` prefix:
+
+```html
+
+
${item/Title}
+
+```
+
+
+(templates-tal-switch-label)=
+
+### `tal:switch` and `tal:case` (Chameleon extension)
+
+Chameleon provides switch/case statements as an extension to standard TAL:
+
+```html
+
+
This is a document.
+
This is a news item.
+
This is an event.
+
This is something else.
+
+```
+
+
+(templates-tales-label)=
+
+## TALES expressions
+
+TALES (TAL Expression Syntax) defines how expressions are evaluated in templates.
+Plone uses **path expressions** as the default expression type.
+
+
+(templates-tales-path-label)=
+
+### Path expressions
+
+Path expressions traverse object attributes and items using `/` as a separator.
+
+```html
+
+
+```
+
+```{warning}
+The `|` operator catches missing attributes and `None` values, but it also catches other errors.
+Use it sparingly to avoid hiding bugs.
+A typo like `context/ttle` (missing 'i') will silently use the fallback.
+```
+
+
+(templates-tales-python-label)=
+
+### Python expressions
+
+Python expressions allow full Python syntax.
+They must be prefixed with `python:`.
+
+```html
+
TITLE
+
+
+ Showing first 10 of ${python:len(items)} items.
+
+
+
+
+ ${item/title}
+
+
+
+
+
+ Title (Type)
+
+```
+
+In filesystem templates, you have full Python access.
+In TTW templates, RestrictedPython limits what you can do.
+
+```{important}
+In Plone templates, the default expression type is `path:`, not `python:`.
+You must explicitly use the `python:` prefix for Python expressions.
+This differs from standalone Chameleon where Python is the default.
+```
+
+
+(templates-tales-string-label)=
+
+### String expressions
+
+String expressions create formatted strings with variable interpolation.
+
+```html
+
+ Edit
+
+
+
+ Greeting
+
+
+
+```
+
+Inside `${...}` you can use any TALES expression, but the default is path.
+
+
+(templates-tales-not-label)=
+
+### The `not:` expression
+
+Negates a boolean expression.
+
+```html
+
+ No description available.
+
+
+
+ No items found.
+
+```
+
+
+(templates-tales-exists-label)=
+
+### The `exists:` expression
+
+Tests whether a path exists without evaluating it.
+
+```html
+
+ Custom field exists: ${context/custom_field}
+
+```
+
+
+(templates-tales-nocall-label)=
+
+### The `nocall:` expression
+
+Returns an object without calling it.
+
+```html
+
+
+ Method: ${python:method.__name__}
+
+```
+
+
+(templates-built-in-variables-label)=
+
+## Built-in variables
+
+Plone templates have access to several built-in variables:
+
+| Variable | Description |
+|----------|-------------|
+| `context` | The content object the view is called on |
+| `view` | The browser view instance |
+| `request` | The current HTTP request object |
+| `template` | The template object itself |
+| `options` | Additional options passed to the template |
+| `nothing` | Equivalent to Python's `None` |
+| `default` | Special value to keep the original content |
+| `repeat` | Dictionary of repeat variables in loops |
+| `attrs` | Original attributes of the current element (in METAL) |
+
+Additional variables available through helper views:
+
+```html
+
+
Portal URL: ${portal_url}
+
User: ${user/getId}
+
Please log in.
+
+```
+
+
+(templates-metal-label)=
+
+## METAL macros
+
+METAL enables template reuse through macros and slots.
+
+
+(templates-metal-define-macro-label)=
+
+### Defining macros
+
+Create a reusable template fragment:
+
+```html
+
+
+
+ ${user/fullname}
+ ${user/email}
+
+
+```
+
+
+(templates-metal-use-macro-label)=
+
+### Using macros
+
+Include a macro in another template:
+
+```html
+
+
+
+```
+
+
+(templates-best-practices-escape-label)=
+
+### Always escape user content
+
+Content is escaped by default.
+Only use `structure` when you trust the content:
+
+```html
+
+
User input
+
+
+
Rich text
+```
+
+
+(templates-best-practices-fallbacks-label)=
+
+### Provide fallbacks
+
+Handle missing or empty values gracefully:
+
+```html
+
+ Description
+
+
+
+```
+
+
+(templates-debugging-label)=
+
+## Debugging templates
+
+
+(templates-debugging-variables-label)=
+
+### Inspecting available variables
+
+Use `context` to see all available variables:
+
+```html
+
+
${name}
+
${python:repr(context[name])[:100]}
+
+```
+
+
+(templates-debugging-pdb-label)=
+
+### Using the debugger
+
+In filesystem templates, you can use pdb:
+
+```html
+
+```
+
+
+(templates-debugging-errors-label)=
+
+### Common errors
+
+**`KeyError` or `AttributeError`**
+: Check for typos in path expressions.
+ Use `exists:` to test if a path exists.
+
+**Unexpected output**
+: Remember that `tal:content` replaces content, `tal:replace` replaces the entire element.
+
+**Expression not evaluated**
+: Ensure you're using the correct expression type prefix (`python:`, `string:`).
+
+**Encoding errors**
+: Ensure your template files are saved as UTF-8.
+
+
+(templates-reference-label)=
+
+## Quick reference
+
+
+(templates-reference-tal-label)=
+
+### TAL statements
+
+| Statement | Purpose | Example |
+|-----------|---------|---------|
+| `tal:define` | Define variables | `tal:define="title context/title"` |
+| `tal:condition` | Conditional rendering | `tal:condition="context/description"` |
+| `tal:repeat` | Loop over items | `tal:repeat="item items"` |
+| `tal:content` | Replace element content | `tal:content="context/title"` |
+| `tal:replace` | Replace entire element | `tal:replace="context/title"` |
+| `tal:attributes` | Set HTML attributes | `tal:attributes="href context/absolute_url"` |
+| `tal:omit-tag` | Remove element, keep content | `tal:omit-tag=""` |
+| `tal:on-error` | Error handling | `tal:on-error="string:Error"` |
+
+
+(templates-reference-tales-label)=
+
+### TALES expression types
+
+| Type | Prefix | Default in Plone | Example |
+|------|--------|------------------|---------|
+| Path | `path:` | Yes | `context/title` |
+| Python | `python:` | No | `python:len(items)` |
+| String | `string:` | No | `string:Hello ${name}` |
+| Not | `not:` | No | `not:context/description` |
+| Exists | `exists:` | No | `exists:context/image` |
+| Nocall | `nocall:` | No | `nocall:context/method` |
+
+
+(templates-reference-metal-label)=
+
+### METAL statements
+
+| Statement | Purpose | Example |
+|-----------|---------|---------|
+| `metal:define-macro` | Define reusable macro | `metal:define-macro="card"` |
+| `metal:use-macro` | Use a macro | `metal:use-macro="context/@@macros/card"` |
+| `metal:define-slot` | Define customizable slot | `metal:define-slot="content"` |
+| `metal:fill-slot` | Fill a slot | `metal:fill-slot="content"` |
+
+
+## See also
+
+- {doc}`views`
+- {doc}`viewlets`
+- {doc}`template-global-variables`
+- [Chameleon documentation](https://chameleon.readthedocs.io/)
+- [Plone Training: Page Templates](https://training.plone.org/mastering-plone-5/zpt.html)