---
title: "Feature: Study plan partials and a markup contract"
manual: "Academic StudyPlan"
version: "main"
source: "Changelog/3.0/Feature-StudyPlanPartialsAndDataAttributes.rst"
rendered: "2026-10-02T15:38:26+00:00"
---

# Feature: Study plan partials and a markup contract {#feature-study-plan-partials-and-data-attributes}

## Description {#description}

The study plan content element rendered every part of itself from one template,
and its script found those parts by class name. An installation that wanted a
different filter, a different module or a module that opens its dialog when it
is clicked anywhere had to replace the whole template - and with it the class
names the script looks for, so it had to fork the script as well.

The template is split into four partials now:

| Partial | Renders |
| --- | --- |
| `StudyPlan/Filter` | The category filter. |
| `StudyPlan/Semester` | One semester column, with its header and its modules. |
| `StudyPlan/Module` | One module, with its dialog trigger. |
| `StudyPlan/ModuleDialog` | The dialog of one module. |

Each of them receives exactly the variables it renders, and each can be
replaced on its own through
`tt_content.academic_study_plan.partialRootPaths` or the constant
`plugin.tx_academicstudyplan.view.partialRootPath`.

The script no longer looks for class names. Every part it drives carries a
`data-study-plan-*` attribute - the filter and the item it clones, the
semester and its header, the module, the dialog and the control that opens it -
and an override that keeps those attributes keeps the whole interaction,
whatever it does to the elements and the classes around them. The attributes,
the element each belongs on and the arguments of each partial are documented in
[Templates](../../Templates/Index.html#templates).

Two things the contract makes possible without a fork:

-   `data-study-plan-dialog-trigger` may sit on the module element
    itself, which makes the whole module open its dialog. The script pairs it
    with the dialog of that very module. It is not the shipped markup, and
    [Templates](../../Templates/Index.html#templates-module-as-trigger) says why.
-   The category filter can be collapsed behind a toggle button, with the new
    site setting `plugin.tx_academicstudyplan.filter.collapsible`
    (`false` by default). The toggle carries `aria-expanded` and
    `aria-controls` and works by keyboard. See
    [Collapse the category filter](../../Configuration/Index.html#collapsible-filter).

## Impact {#impact}

**The default output is unchanged.** An installation that configures nothing
and overrides nothing renders the same text, the same elements and the same
classes as before, with the data attributes added - and behaves the same by
mouse and by keyboard.

## Affected Installations {#affected-installations}

Every installation that renders the study plan content element. None of them
has anything to do on update.

An installation that forked `AcademicStudyPlan.html` or the script can
move onto the partials and the attributes instead, and stop carrying the copy.
Markup that identifies its parts only by the class names of 3.0 keeps working
for the whole 3.x line - see
[Deprecation: The study plan class selectors](Deprecation-StudyPlanClassSelectors.html#deprecation-study-plan-class-selectors).
