Styling
The academic extensions render their frontend markup without a look of its own. Every element carries a class that says what it is, the classes follow the same system in every extension, and the extensions ship no stylesheet for them. The look is the job of the site package of a project.
This chapter describes that system once for all academic extensions. The chapter Styling of each extension shows how the markup of its plugins is built up, see The markup of each extension.
What the extensions ship
- Markup and classes. The templates, partials and layouts below
Resources/of each extension. Their classes are described below.Private/ - Behaviour. The JavaScript modules that make a list filter in place,
open a dialog of the study plan or run the profile editor. They find their
elements by
data-*attributes, with two exceptions, and each extension can switch its scripts off per site, see Scripts and classes. - No styling. No extension ships a stylesheet for its markup. The
stylesheets an extension loads are those of a library one of its scripts
needs to work, the map library of the partner map, and they are loaded and
switched off together with that script. The one other case is the
standalone set
fgtclb/of EXT:academic_persons, an opt-in page object for an installation without a site package of its own, which loads the stylesheet and the script of Bootstrap 5 from jsDelivr. It renders a plain Bootstrap page around the plugins and does not style their markup.academic- persons- standalone
A project that installs an academic extension and adds no styles of its own sees the markup as its theme styles plain HTML: headings, lists, links, form fields and images, laid out by the grid classes the markup keeps (Classes of Bootstrap and TYPO3 kept on purpose).
The class system
An element carries up to three kinds of class:
- The outermost element of a plugin or page names the extension and the
plugin,
academic-<extension>-<plugin>. - Speaking classes with the prefix
ace-name what an element is, the same in every extension. - A few classes of Bootstrap and TYPO3 are kept on purpose, because a TYPO3 site styles them already.
The outermost element
Every plugin renders one outermost element with a class of the pattern
academic-<extension>-<plugin>, every page of a page type of an extension one
with academic-<extension>-:
<div class="academic-jobs-list">…</div>
<div class="academic-programs-page container">…</div>
A few outermost classes do not follow the pattern exactly:
- The partnerships list and teaser of EXT:academic_partners,
academic-andpartnerships- list academic-, name the partnerships rather than the extension.partnerships- teaser - The profile editor of EXT:academic_persons_edit is
academic-. Its profile list carries two classes,persons- profile- editing academic-.persons- edit academic- persons- profile- editing- list - The study plan is a content element without plugins and carries
academic-.study- plan
The outermost class is the only class that tells the extensions and their plugins apart, and it is therefore the scope of every rule of a project stylesheet, see Styling the markup in a project. The outermost element of each plugin is listed in the chapter Styling of its extension.
A plugin leaves its width to the layout of its content element and carries no
container. A page of a page type, a program, partner or project page, renders
the main content of the site layout and carries container next to its class.
Speaking classes
A speaking class names the thing an element is, not the look it has in a
theme: ace- is one record of a list, whatever extension renders it, and
ace- the label of a value, whether it sits in a card, a table or a form.
ace stands for academic extensions.
An element carries the general class first and a more specific one next to it
where there is one: the attributes of an item are an ace-
of ace- entries, a filter is an
ace-. A stylesheet that styles every list of
a plugin selects ace-, one that styles only the attributes selects
ace-.
| Family | Classes | Meaning |
|---|---|---|
| Content | ace-, ace-, ace- | The content of a plugin below the header of its content element, or of a part of it below its own head, a section with a title of its own, and the head of a page with its title and media. |
| Items | ace-, ace-, ace-, ace- | A list of records, one record, an
<article>
or a table
row, the text of a record next to its image, and the message of a
list without records. |
| Lists | ace-, ace- | Any list, and one entry of it, also the parts of a value that has several, the addresses of a contact for example. |
| Attributes | ace-, ace-, ace-, ace-,
ace- | The properties of a record, one property, its label, its value and the icon in front of it. |
| Headings | ace-, ace-, ace-, ace- | The head of an element, mostly the
<header>
around a
heading, the heading or title of a record, a section or a dialog,
the subtitle below it, and a name of a person. |
| Links | ace- | A link that is not a button: the link back to a list, the link of an active filter, the link that resets the filters. |
| Actions | ace-, ace- | The group of buttons or controls of an element. |
| Dialogs | ace-, ace- | A
<dialog>
, and the button that closes it or a message. |
| Forms | ace-, ace-, ace-<type>-, ace-,
ace-, ace-, ace-<type>, ace-,
ace-, ace-, ace- | A form, one field with its label, the required mark, the control and its type, the help text and the messages of a field that failed validation, and the message above a form with such a field. See A form field. |
| Filters | ace-, ace-, ace-, ace- | The filter and sorting bar of a list, one filter, the
<details>
element that holds the filters beyond the visible
ones, and its
<summary>
. |
| Active filters | ace-, ace- | The list of the filters a visitor applied, next to ace-, and
one of them, next to ace-. Each holds an ace-
that removes it, with an ace-. |
| Count and status | ace-, ace- | A number, the number of results of a list for example, and the
live region a script announces a changed result in. The live
region is visually- as well. |
| Pagination | f3- | The pages of a list, see Classes of Bootstrap and TYPO3 kept on purpose. |
| State | ace- | The state of a record shown as text, with the state as a second
class, ace- for a running project for example. |
| Contact | ace-, ace- | A block of contact data, and the role of a contact person. |
| Media | ace-, ace-, ace-, ace-,
ace-, ace-, ace- | An image, the
<picture>
around its sources, the
<figure>
of an image with a caption, the caption with its
description and copyright, and a map. See
An image. |
| Tables | ace-, ace-, ace-, ace- | A table view, its rows and its header and data cells. |
An extension with markup of its own adds a family with the same prefix, the
semesters and modules of the study plan, ace- and ace-, for
example. Those are described in the chapter of that extension.
States
A state an element is in is a class without prefix, or an HTML attribute:
| State | Meaning |
|---|---|
active | The current entry of a navigation or pagination. |
disabled | An entry of a navigation or pagination that cannot be followed. |
invalid | A form field, and its wrapper, that failed validation, with
aria- on the field. The profile editor of
EXT:academic_persons_edit names this state
is-, the name of Bootstrap, see its chapter. |
open
| The attribute of an open
<details>
or
<dialog>
. |
hidden
| The attribute of an element a script shows when it is needed. |
A state a script sets is named in the chapter of the extension the script belongs to.
Classes of Bootstrap and TYPO3 kept on purpose
The markup keeps a few classes it did not invent. They belong to systems a
TYPO3 site provides already, either through Bootstrap, which the most used
site packages build on, or through TYPO3 itself. A project with such a theme
gets layout, buttons and accessible hiding without a line of its own. The
markup uses no other class of Bootstrap: no cards, no spacing or display
utilities, no form classes. The one exception is the flash message of the job
list, the job detail and the job form of EXT:academic_jobs, the
confirmation after a submitted job among them. It is rendered by TYPO3
itself, as a
<ul class="typo3- of
<li class="alert alert-<severity>">
entries, with the alert classes of Bootstrap.
| Classes | Why they are kept |
|---|---|
container | The content width of a page of a page type. A plugin carries none, its width is the one of its content element layout. |
row, col-* | The grid of the item lists, the filter bars, the forms and the columns of a detail view. A responsive grid is layout, not look, and every Bootstrap theme provides it. |
btn, btn-* | The buttons of a form and links that act as a button, the
application link of a program for example, with the button group
btn- and the close button btn- of the profile editor.
A theme styles its buttons through these classes, so the buttons of
the plugins look like those of the site. Where the plugin has to
tell its button from others, a speaking class stands next to them. |
visually- | Text that a screen reader reads and a visitor does not see, the live region of a list for example. Hiding it is accessibility, not look, and must work on every site. |
f3-, page-, page- | The list of pages of a pagination is an f3-, the
name the pagination of TYPO3 uses. The paginations of the job and
partner lists name their entries page- and page-, as
Bootstrap does. The pagination of the profile list names them
ace- and ace- and its
<nav>
ace-. The entries carry first, previous, next and
last, the current one active, the gaps disabled. |
ce- | The class the core templates of fluid_styled_content give the text of a content element. Every field the extensions render from the rich text editor is wrapped in an element with this class, so the content styles of the site apply to it. |
A site without Bootstrap defines these few classes itself. visually-
is the one that must not be left out, or text meant for screen readers shows on
the page:
.visually-hidden {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
Icons
The icons of the frontend are the shared icons of this extension, the
tx- identifiers, and a few of the extensions themselves, see
Icons. The icon view helper of this extension renders them with the
markup of the icon API of TYPO3, the same as
<core: renders: a
<span>
with icon, icon- and icon-<identifier> around
an icon- holding the inline SVG.
The shipped icons are drawn in current and are 1em wide and high, so
they take the colour and the font size of the text around them. The templates
wrap an icon in an ace- where it belongs to an attribute or a contact
line, so a stylesheet colours, sizes and aligns the icons of a plugin through
.ace-. A site package that wants another glyph replaces the icon itself,
see Replacing an icon in a site package, not the template.
Scripts and classes
The JavaScript modules of the extensions find the elements they work with by
data-* attributes and ids. A project may therefore add, remove or rename a
class in a template override without breaking a script. An override has to
keep the data-* attributes and ids of the elements it copies.
Two scripts find an element by its class, and an override keeps that class:
- The rich text editor of the new job form of EXT:academic_jobs
starts on every text area with the class
ace-.ckeditor - The profile editor of EXT:academic_persons_edit writes the
message of a refused field into the
ace-of the field or its group.message
A few classes are written by a script, to mark a state a stylesheet should show. The chapter of the extension names them.
Every extension that loads a script has one switch for it per site, the site
setting or TypoScript constant plugin., for example
plugin.. Switched off, the page loads neither
the script nor the stylesheets of a library it needs, and the markup stays as
it is, for a script of the project to drive. Each extension describes its
switch in its chapter Configuration.
Styling the markup in a project
A stylesheet in the site package
The styles belong in the site package of the project, as one more stylesheet of it, loaded like the others. With TypoScript:
page.includeCSS.academic = EXT:site_package/Resources/Public/Css/academic.css
Or in the page template of the site package, so it is only loaded with the page layout that needs it:
<f:asset.css
identifier="siteAcademic"
href="EXT:site_package/Resources/Public/Css/academic.css"
/>
Scope every rule by the outermost element
The speaking classes mean the same in every extension, so a rule for .ace-
alone styles the items of every academic plugin on the site. Scope a rule by
the outermost element of the plugin it is meant for, and leave it unscoped only
where every plugin should look the same:
/* The same for every academic plugin */
.ace-attribute .ace-label {
font-weight: 600;
}
/* The program cards only */
.academic-programs-list .ace-item {
display: flex;
flex-direction: column-reverse;
height: 100%;
border: 1px solid var(--bs-border-color, #dee2e6);
}
/* The job detail only */
.academic-jobs-detail .ace-attributes {
list-style: none;
padding-left: 0;
}
A theme built with SCSS on Bootstrap can give the speaking classes the look of its own components rather than repeating their declarations:
.academic-programs-list .ace-item {
@extend .card;
}
.academic-programs-list .ace-item-content {
@extend .card-body;
}
A complete stylesheet as a reference
The mono repository the academic extensions are developed in carries a
development site package, academics_, and its stylesheet is a
working example of the approach above. It is never shipped with an extension.
Its sources are a shared SCSS partial and one partial per extension below
packages-, collected by
academic- and compiled into
packages-,
which the development instances include on every page with
page.:
| Partial | Styles |
|---|---|
| _academic-base.scss | What several extensions share: the colours and sizes as custom properties, item lists and their items, attribute rows, the keyboard focus, filters, the letter navigation and the pagination, the hero and the facts of the page templates, and the layout of the page tree without a theme. |
| _academic-jobs.scss | The jobs of academic_ and academic_, and the
form for a new job. |
| _academic-partners.scss | The partner views and the partner map. |
| _academic-persons.scss | The profile detail. |
| _academic-persons-list.scss | The profile lists and cards, and the contacts of a page. |
| _academic-persons-edit.scss | The profile editor. |
| _academic-programs.scss | The program views and the program page. |
| _academic-projects.scss | The project views and the project page. |
| _academic-study-plan.scss | The study plan. |
The stylesheet is written against the markup and the Bootstrap 5 classes it
carries, on top of the Bootstrap Package, the theme of the main page tree of
the instances. The second page tree of the instances has no theme, so the
shared partial also lays the plugins out there, below an attribute of the page
body, which shows what a site without Bootstrap has to provide. The colours,
the radius and the gaps are custom properties declared once in
_academic-, so a site changes the look in one place. The
profile editor is the one exception: its partial declares colours of its own.
Override a template only to change the markup
A look is changed with a stylesheet. A template override is the tool for a change of the markup itself, a field the template does not render or an element in another order, and it has a price: the copy misses every correction made to the original. The extensions therefore build their templates from small partials, so an override replaces the piece that has to change and nothing else. Each extension describes its partials in its chapter Templates, the shared partials of this extension are described in Templates.
The classes are part of the public API of an extension. A class is renamed or removed only with a breaking changelog entry, which names the class before and after, so a project stylesheet can follow.
The markup of this extension
This extension renders no plugin of its own. It ships partials the other extensions render inside their plugins: the fields of a frontend form and the responsive image. Their arguments are described in Templates.
A form field
A field partial of Partials/ renders a field like this,
here a text field that failed validation:
<div class="ace-field invalid ace-text-wrap">
<label class="ace-label" for="job.title">
Title
<abbr class="ace-required" title="required">*</abbr>
</label>
<input class="ace-control ace-text invalid" id="job.title" aria-invalid="true"
aria-describedby="job.title-error" … />
<div id="job.title-error" class="ace-field-errors">
<div class="ace-field-error">…</div>
</div>
<div class="ace-help">…</div>
</div>
The wrapper carries the class of the type of the field, ace-,
ace-, ace-, ace-, ace- or
ace-, and the control ace- with its type, ace-,
ace-, ace-, ace-, ace- or ace-. A
text area with the rich text editor carries ace- as well. The
messages and the help text are only rendered when there are any. A form with a
field that failed validation shows its message above the form in an
ace-.
The filter selects of the list plugins follow the same names, an ace-
next to ace- with an ace-, so a
stylesheet that styles the form fields styles the filters as well.
An image
The image partial Partials/ renders a raster image
as a
<picture>
and an SVG image or the placeholder as a single
<img>
:
<picture class="ace-picture">
<source … />
<img class="ace-image" … />
</picture>
An image with a caption or a copyright is wrapped in a figure:
<figure class="ace-figure">
<picture class="ace-picture">…</picture>
<figcaption class="ace-caption">
<span class="ace-description">…</span>
<small class="ace-copyright">© …</small>
</figcaption>
</figure>
A template that renders the partial may pass a class of its own, which the
<img>
carries after ace-.
The markup of each extension
How the plugins of an extension are built up, with their outermost element and the classes they add to the system above, is described in the chapter Styling of each manual: