---
title: "Templates"
manual: "Academic StudyPlan"
version: "3.0"
permalink: "https://docs.typo3.org/permalink/fgtclb/academic-study-plan:templates@3.0"
source: "Templates/Index.rst"
rendered: "2026-10-26T17:55:56+00:00"
---

# Templates {#templates}

The study plan content element renders from one template and four partials, and
its script finds every part it drives by a `data-study-plan-*` attribute.
Together those are the contract: replace any partial, with your own elements and
your own class names, keep the attributes where this page puts them, and the
filter, the highlighting, the semester accordion and the module dialogs keep
working.

-   [Where they live](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:where-they-live@3.0)
-   [What a partial is given](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:what-a-partial-is-given@3.0)
-   [The data attributes](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:the-data-attributes@3.0)
-   [The attributes the script reads a value from](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:the-attributes-the-script-reads-a-value-from@3.0)
-   [The glyphs of a semester header](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:the-glyphs-of-a-semester-header@3.0)
-   [The module element as the trigger](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:the-module-element-as-the-trigger@3.0)
-   [Markup of version 2.x keeps working](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:markup-of-version-2-x-keeps-working@3.0)
-   [What a page without the script shows](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:what-a-page-without-the-script-shows@3.0)
-   [See also](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:see-also@3.0)

## Where they live {#templates-where}

| File | Renders |
| --- | --- |
| `Frontend/Default/Templates/AcademicStudyPlan.html` | The container, and the script of [Skip the script](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:asset-switches@3.0). |
| `Frontend/Default/Partials/StudyPlan/Filter.html` | The category filter. |
| `Frontend/Default/Partials/StudyPlan/Semester.html` | One semester column, with its header and its modules. |
| `Frontend/Default/Partials/StudyPlan/Module.html` | One module, with its dialog trigger. |
| `Frontend/Default/Partials/StudyPlan/ModuleDialog.html` | The dialog of one module. |

All five sit below
`EXT:academic_study_plan/Resources/Private/`. Point the element at
templates of your own with the three constants
`plugin.tx_academicstudyplan.view.templateRootPath`,
`.partialRootPath` and `.layoutRootPath`, or add a
higher entry to `tt_content.academic_study_plan.partialRootPaths`.
A path that holds one file replaces that one file; everything else keeps coming
from the extension.

## What a partial is given {#templates-arguments}

Each partial receives exactly the variables it renders, so an override has an
argument list it can rely on rather than whatever the template happened to have
in scope.

| Partial | Arguments |
| --- | --- |
| `StudyPlan/Filter` | `collapsible` \- whether the site asked for the collapsible filter, see [Collapse the category filter](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:collapsible-filter@3.0). |
| `StudyPlan/Semester` | `semester` \- the semester, with its `modules`, and `idPrefix`, which it passes on to the modules. |
| `StudyPlan/Module` | `module` and the `semester` it belongs to, the latter only for the label the dialog trigger announces, and `idPrefix`, which the dialog id starts with. |
| `StudyPlan/ModuleDialog` | `module` and `idPrefix`. |

`idPrefix` is empty for a plan placed on the page, so its dialog ids are
`popup-<module uid>`. A plan rendered inside another content element - an
"Insert records" element, or a grid element that renders its children through
`RECORDS`, `CONTENT`, `f:cObject` or, on TYPO3
v14, `f:render.record` \- gets `c<uid>-` of that element, so its
dialogs are `popup-c<uid>-<module uid>` and no copy of a plan repeats the
dialog ids of another. An override that does not pass `idPrefix` on
renders the ids without it and keeps working, see
[data-dialog-id](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:templates-attribute-values@3.0).

The `credit_points` of a semester and of a module are a number, not the
value the database returns: printed as they are, they read "2.5", "30" and
"0" rather than "2.50", "30.00" and "0.00". They render with a decimal point on
every page language; format them in the override for a decimal comma.

## The data attributes {#templates-attributes}

| Attribute | Belongs on | What the script does with it |
| --- | --- | --- |
| `data-study-plan` | The element that wraps the whole plan. | Starts one instance on it. The value is the uid of the content element; several plans on a page are told apart by the element itself, not by the value, so it may repeat. |
| `data-study-plan-filter` | The list the filter buttons go into. | Empties it and fills it with one button per category the rendered modules carry. |
| `data-study-plan-filter-template` | The single item inside that list. | Clones it per category, substituting `category-id-placeholder`, `category-color-placeholder` and `category-label-placeholder`, and takes `hidden` off every clone. |
| `data-study-plan-filter-collapsible` | The same list as `data-study-plan-filter`, and only when the site switched the collapsible filter on. The extension renders it for you. | Hides the list and puts a toggle button in front of it, labelled from `data-filter-label`. Without that label, or with no category in the filter, no toggle is inserted and the filter stays expanded. |
| `data-study-plan-semester` | The element that holds one semester with its modules. | Opens and closes it, and highlights it with its modules. |
| `data-study-plan-semester-header` | The header inside a semester. | Makes it the accordion control below 768 pixels and inert above it. |
| `data-study-plan-module` | One module inside a semester. | Reads its `data-categories`, highlights it, levels its height on a wide viewport, and looks for its dialog and trigger inside it. |
| `data-study-plan-dialog-trigger` | The control that opens the dialog of a module, inside that module - or the module element itself, see below. | Opens the dialog of that module, by pointer and by keyboard. The dialog is the one named by `data-dialog-id`, looked up inside the plan first, and the module's own when the trigger names none. |
| `data-study-plan-dialog` | The `<dialog>` of a module, inside that module. | Opens it as a modal and closes it again from the first `<button>` inside it, stopping whatever it was playing. |

Two class names are **not** part of this list and are written by the script
rather than looked up: `highlighted` and `open`, on the container,
the semesters and the modules. Style them, do not rely on them being absent.

The script also writes `hidden` on the filter list while it is collapsed.
That attribute only hides anything when the stylesheet of the site says
`.ace-filter .ace-list[hidden] { display: none }` \- the browser's own rule
for it loses to any author rule that gives the list a `display`. The
extension ships no stylesheet, see [Style the element](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:styling@3.0).

## The attributes the script reads a value from {#templates-attribute-values}

The attributes above are markers. These four carry values the script needs, and
an override that renders modules or filter buttons itself has to produce them:

| Attribute | Belongs on | Value |
| --- | --- | --- |
| `data-filter-label` | The container. | The label of the collapsible filter's toggle button. The extension renders the translation of `filter.label` into it. |
| `data-categories` | A module. | A JSON array of the categories the module carries, each an object with `uid`, `label` and `colour` \- for example `[{"uid":1,"label":"Mandatory","colour":"#cc0000"}]`. An absent attribute, an empty string and `[]` all mean "no category"; anything that does not parse is treated the same way. The title is rendered as text, never as markup. |
| `data-category-id`, `data-category-color` | The `<button>` inside the filter template item. | Substituted from `category-id-placeholder` and `category-color-placeholder` when the item is cloned, and read back when the button is activated. A button without them highlights nothing. A colour that is not a hexadecimal value, a plain keyword or an `rgb()` / `rgba()` function is dropped. |
| `data-dialog-id` | A dialog trigger. | The `id` of the dialog it opens. The script looks for it among the dialogs of its own plan first, and in the whole document only when the plan has none, so the same plan shown twice opens the dialog of each copy even with repeated ids, and a dialog an override renders outside the plan is still found. Without it the trigger opens the dialog inside its own module. |

The three placeholder strings - `category-id-placeholder`,
`category-color-placeholder` and `category-label-placeholder` \- are
substituted wherever they appear in the item, in an attribute value or in text.
Keep them in an override of the filter, in the attributes above and wherever the
label is to be read.

## The glyphs of a semester header {#templates-glyphs}

The semester partial renders two glyphs into the header, inside a
`<span class="ace-actions">`, `tx-academicbase-action-expand` and
`tx-academicbase-action-collapse`, and the dialog partial renders
`tx-academicbase-action-close` into its close button. All three are shared
action icons of **EXT:academic_base**: inlined SVGs drawn in
`currentColor`, rendered with its `ab:icon` ViewHelper from its
frontend icon registry. An override that renders them declares
`xmlns:ab="http://typo3.org/ns/FGTCLB/AcademicBase/ViewHelpers"` in its
`<html>` tag. With `core:icon` it shows TYPO3's not-found icon, see
[Breaking: The study plan icons are renamed and the controls are frontend icons](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:breaking-study-plan-control-icons-moved-to-the-frontend-icon-registry@3.0).

The script does not look at the glyphs, the stylesheet of the site does. The
example of [Style the element](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:styling@3.0) selects the classes the
ViewHelper writes: `icon-tx-academicbase-action-collapse` is hidden in a
closed semester, `icon-tx-academicbase-action-expand` in an open one, and
every `.icon` in the header from 768 pixels on. It also sizes every
`.icon` of the element to `1.25rem` and the drawing inside it to the full
icon, so a drawing without a size of its own stays visible. An override of
`StudyPlan/Semester.html` for a stylesheet like that one therefore keeps
rendering the two identifiers, with `ab:icon`. One that renders glyphs of its
own brings the rules for them.

To draw a glyph differently, register a file of your own under its identifier
in the `Configuration/FrontendIcons.php` of your site package, which has
to depend on **EXT:academic_base** so that its entry is read last. The
identifiers belong to **EXT:academic_base**, so the replacement shows
in every academic extension that renders the glyph, not in the study plan
alone. An entry in [`Configuration/Icons.php`](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ExtensionArchitecture/FileStructure/Configuration/Icons.html#file-extension-configuration-icons-php) does not reach the study
plan.

## The module element as the trigger {#templates-module-as-trigger}

`data-study-plan-dialog-trigger` may sit on the module element itself
instead of on a control inside it, which makes the whole module open its
dialog. The script pairs it with the dialog of that very module, so a plan full
of such modules opens the right one each time.

The script does not make that element focusable, and an override that wants
this has to: give it `tabindex="0"` and `role="button"` itself,
or the dialog opens by pointer only. A key pressed on something *inside* the
module - a link, an audio control - is left to that control and does not open
the dialog.

The extension does not render it that way, and does not offer a setting for it:
a module that is itself a control nests its heading, its credits and its dialog
inside interactive content, which assistive technology reads differently and
which no keyboard order describes well. It is supported because an installation
that wants it should not have to fork the script for it.

## Markup of version 2.x keeps working {#templates-2-x}

Before these attributes existed the script found its parts by class name:
`.academic-study-plan`, `.filter`, the first `<li>` inside it,
`.col`, `.header`, `.module`, `.modal-trigger` and the
`<dialog>` inside a module. Each of those *inside* a plan is still read,
per part, when the attribute of that part is nowhere in that plan. So an
override written for 2.x keeps working, and one that carries the attributes on
some parts and not on others works too.

The container is the exception: `.academic-study-plan` is read **next to**
`data-study-plan` rather than instead of it, because two plans of one page
can come from different templates, and one whose template lacks the attribute
must not disappear because the other one carries it.

That fallback is deprecated and is removed in 4.0. See
[Deprecation: The study plan class selectors](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:deprecation-study-plan-class-selectors@3.0).

## What a page without the script shows {#templates-no-script}

The markup is not a fallback: the filter, the accordion and the dialogs need a
script. The one part that would otherwise be visible is handled - the item
inside the filter list is rendered `hidden`, because its placeholder text
is not meant for anybody. An installation that brings a script of its own has to
take that attribute off the items it builds.

## See also {#templates-see-also}

-   [Configuration](https://docs.typo3.org/permalink/fgtclb/academic-study-plan:configuration@3.0) \- the site settings and constants this
    page refers to.
