Diese Seite dokumentiert die wiederverwendbaren Bausteine der App. Halte dich an diese Vorgaben, damit alle Ansichten einheitlich im h_da Corporate Design bleiben.

Farben

Die zulässige h_da-Palette. Immer über die CSS-Variablen ansprechen, nie Hex-Werte hartkodieren. Rolle vor Palettenfarbe: die Akzentfarbe der App liegt im Slot --hda-brand / --hda-brand-dark / --hda-brand-rgb (aktuell h_da-Orange) — Chrome-Elemente greifen auf diesen Slot zu, nicht direkt auf --hda-orange. Ein Farbwechsel ist dann ein Einzeiler in hda-theme.css.

Grün
#9DC41E
--hda-green
Grün dunkel
#719E3E
--hda-green-dark
Orange
#E98011
--hda-orange
Primär · Akzent
Orange dunkel
#CF6F0C
--hda-orange-dark
Blau
#519CDB
--hda-blue
Blau dunkel
#102D5C
--hda-blue-dark
Rot
#AC1B21
--hda-red
Gelb
#FCCB06
--hda-yellow
Violett
#A04AA3
--hda-purple
Bordeaux
#5D0613
--hda-bordeaux
Anthrazit
#231F20
--hda-anthracite
Text

Akzentfarbe (Farb-Slot)

Die Akzentfarbe der App ist h_da-Orange und liegt im Slot --hda-brand / --hda-brand-dark / --hda-brand-rgb. Die gesamte Chrome (Buttons, Links, Nav-Aktiv, Tabs, Brand-Akzent, Form-Fokus) greift auf diesen Slot zu, nicht direkt auf --hda-orange — ein Farbwechsel bleibt damit ein Einzeiler in hda-theme.css.

  • Farben immer über die CSS-Variablen ansprechen, nie Hex/rgba hartkodieren.
  • Dekorative Tints als rgba(var(--hda-brand-rgb, 233, 128, 17), X) — der Fallback 233, 128, 17 ist Pflicht (Templates sind ungecacht, die CSS nicht; ohne Fallback „verschwindet" die Akzentfarbe bei veralteter CSS).
  • Der Kontrast-/Barrierefreiheitsmodus (.theme-contrast, Schwarz-Weiß) hat Vorrang: er setzt --hda-brand* auf reines Schwarz und überschreibt damit den Akzent.

Typografie

Hausschrift HDA DIN (--hda-font-sans), mit System-Sans als Fallback.

h1 · 2rem

Überschrift Ebene 1

h2 · 1.7rem

Überschrift Ebene 2

h3 · 1.25rem

Überschrift Ebene 3

Fließtext

Der schnelle braune Fuchs springt über den faulen Hund – 0123456789 äöüß.

Lead

Hervorgehobener Einleitungstext für Info-Boxen und Willkommensbereiche.

Klein / Muted
Sekundärer Hinweistext, z. B. Metadaten.
Schriftschnitte
Regular 400
Medium 500
Bold 700

Buttons

Jeder Text-Button trägt ein führendes Tabler-Icon, das die Aktion andeutet – gefolgt von einem kleinen Abstand von 0.40em zum Text (app-weit über .btn:not(.btn-icon) > .ti:first-child gesetzt, kein Leerzeichen im Markup nötig).

Varianten (Löschen: btn-outline-danger + data-confirm-delete — 1. Klick füllt, 2. Klick löscht.)
Größen
Icon-only (Icon-only Buttons brauchen kein Label-Icon zusätzlich.)

Icons & Aktionen

Ausschließlich Tabler Icons (<i class="ti ti-NAME">) — nie Font Awesome / Bootstrap Icons. Der Icon-Webfont ist über layouts/base.html global geladen; Standalone-Templates, die layouts/base.html nicht erweitern (z. B. Login-/Standalone-Seiten), binden den Webfont {% static 'tabler-icons/tabler-icons.min.css' %} (vendored unter app/static/tabler-icons/) selbst ein. Verbindliche Aktion→Icon-Zuordnung (bei fehlender Passung nächstliegendes Icon, Fallback ti-chevron-right):

Speichern ti-device-floppy
Abbrechen/Schließen ti-x
Löschen ti-trash
Bearbeiten ti-pencil
Hinzufügen/Neu ti-plus
Suchen ti-search
Filtern ti-filter
Zurücksetzen ti-rotate
Senden ti-send
Download ti-download
Upload ti-upload
Zurück ti-arrow-left
Weiter ti-arrow-right
Anmelden ti-login
Abmelden ti-logout
Teilen ti-share
Kopieren ti-copy
Ansehen/Details ti-eye
Bestätigen/Ja ti-check
Einstellungen ti-settings
Kacheln ti-grid-dots
Tabelle ti-table

Icon-only-Controls (Schließen ×, Toolbar-Glyphen, Theme-/Nav-Toggle) tragen kein zusätzliches Label-Icon. Text-Buttons: Icon→Text-Abstand 0.40em app-weit via .btn:not(.btn-icon) > .ti:first-child — nicht pro Button hand-tunen.

Badges & Status

Helle bg-*-lt-Badges für Status/Kategorien (Standard), gefüllte bg-* nur für starke Signale. Für einen reinen Punkt-Indikator (ohne Text) der status-dot mit Tooltip.

Aktiv In Prüfung Info Hinweis Kritisch Extern Inaktiv Voll Voll Voll

Status-Dot (Punkt-Indikator)

Kleiner farbiger Punkt mit Tooltip statt Text-Badge — für dichte Statuslisten (z. B. Abruf-Status je Hochschule). Vier Zustände; hier als Demo unter .sg-status-dot — beim ersten echten Einsatz nach hda-theme.css ziehen.

OK (grün) Fehler (rot) Geändert (gelb) Unbekannt (grau)

Avatare

Zwei Systeme: Tabler .avatar (Initialen oder Icon, mit Tönungs-Klasse bg-*-lt + passender Textfarbe, Größen avatar-sm/avatar-lg) für Nutzer/Icons; und der custom .program-avatar (Abschluss-Initiale B/M, is-master = orange) für Studiengänge.

Tabler .avatar

MM JD Initialen (Header-Menü) oder Icon; Tönung + Textfarbe kombinieren.

Studiengang .program-avatar

B M Abschluss-Initiale; is-master färbt orange (Demo: .sg-prog-avatar).

Cards

Standard-Card

Tabler-Card mit Header und Body – Basis für Tabellen, Formulare und Detailansichten.

Portal-Card (Akzent)

Begrüßungs- und Info-Boxen. Verlauf in der Marken-/Primärfarbe.

Portal-Card (Orange)

Abgesetzte Bereiche für Mitarbeitende – oranger Verlauf als Zielgruppen-Signal.

Card-Header

Der .card-header ist ein Flex-Container: links der .card-title, rechts optional .card-actions (Badge, Meta-Text, Button, Icon-Button, Suchfeld). Ein Icon im Titel steht mit <i class="ti … me-2"> davor (Titel-Abstand, nicht die 0.40em-Button-Regel).
Überschriften-Ebene: .card-title ist eine Optik-Klasse – das HTML-Tag folgt der Seitenhierarchie. In normalen Seiten liegt die Karte direkt unter dem <h2>-Seitentitel → <h3 class="card-title"> (App-Standard). Hier im Styleguide stehen die Demos unter den <h3>-Abschnitten, also eine Ebene tiefer als <h4>.
App-Standard für Aktionen im Header: kleine Buttons (btn-sm) mit führendem Icon.

Darstellung

Nur Titel
Kein Icon, keine Aktion – häufigster Fall (z. B. Einstellungen-Karten, Detailkarten).

Einstellungen

Titel mit Icon
Führendes Tabler-Icon mit me-2 (z. B. KI-Assistent, Studiengang-Karten).

128 Einträge

Ohne Titel-Text (Badge)
Titel ist nur ein Status-/Zähler-Badge + Aktion (z. B. erfasste Studiengänge).

Stand der Kohorten

7 Kohorten
Titel + Badge
Rechts ein Zähler-/Status-Badge (z. B. Kohorten, Nachrichten).

Datenabruf

Informatik B.Sc.
Titel + Meta-Text
card-actions text-muted small als Kontext rechts (z. B. Studiengang, CP-Summe).

Einstellungen

Titel + kleiner Button Standard
btn btn-primary btn-sm mit Icon – die empfohlene Header-Aktion.

6 Studierende

Titel + Icon-Button(s)
btn-icon ohne Label für Toolbar-Glyphen (z. B. Vollbild, Ansicht wechseln).

BBPO-Versionen

Titel + großer Button
Volle Button-Größe (ohne btn-sm) für die zentrale Aktion einer Seite – sparsam einsetzen.

Vorhandene Planungen

12 Einträge
Titel + Suchfeld
Such-/Filter-Input + optionaler Count-Badge rechts (card-actions d-flex align-items-center gap-2, input-icon + form-control-sm). Aktions-Buttons in dieser Gruppe als btn-sm, damit sie mit dem Suchfeld fluchten.
Header mit Tabs
nav-tabs card-header-tabs statt Titel – für gruppierte Ansichten (z. B. Admin).

Anmelden

Zentrierter Header
text-center justify-content-center – für Login-/Auth-Karten.

Tabellen

Basis: table table-vcenter (Zellen vertikal zentriert). In einer Karte zusätzlich card-table (bündig an den Kartenrand), table-hover für klickbare Zeilen, table-sm für kompakte Listen, table-bordered für Kreuztabellen/Matrizen, table-borderless für randlose. Breite Tabellen immer in <div class="table-responsive"> wickeln (horizontales Scrollen statt Umbruch).
Konvention: statt einer Action-Spalte die erste Spalte klickbar machen (Link, wie Bot-Tabellen); wo Zeilen-Aktionen nötig sind, rechts Icon-Buttons (btn-sm btn-icon) und Löschen als btn-outline-danger + data-confirm-delete. Zahlen rechtsbündig (text-end), Status als Badge.

Standard-Datentabelle

card-table · table-vcenter · table-hover
StudiengangKürzelStatusCP
Informatik
B.Sc.
fbi_inf_bsc Aktiv 210
Informatik dual
B.Sc.
fbi_infdl_bsc In Prüfung 210
Data Science
M.Sc.
fbi_ds_msc Inaktiv 90

Mit Aktionen-Spalte

NameAktion
Prüfungsordnung BBPO21
Prüfungsordnung BBPO18

Kompakt (table-sm)

ModulSem.CP
Programmieren 116
Mathematik 118
Datenbanken36
Software Engineering46

Matrix (table-bordered)

Kohorte1. Sem2. Sem3. Sem4. Sem
2023W48454112
2024W5249

Datagrid (Key-Value)

Tabler .datagrid für Schlüssel-Wert-Metadaten in Detailansichten (statt einer zweispaltigen Tabelle) — ein responsiv umbrechendes Raster aus datagrid-item (datagrid-title + datagrid-content).

Fachbereich
Informatik
Studienform
Vollzeit
Studierende
248

Fortschrittsbalken

Tabler .progress + .progress-bar; schlank ohne Text als .progress-sm. Farbe i. d. R. bg-primary, für Risiko/Ampel dynamisch bg-{{ risk_color }} (green/orange/red). Eingesetzt für ECTS-Fortschritt und Erfolgsquoten.

68%

List-Group & Accordion

List-Group

Schlichte Zeilenlisten (Noten, Zuordnungen) — meist list-group-flush + d-flex justify-content-between; klickbare Zeilen als list-group-item-action.

Programmieren 11,3
Mathematik 12,0
Datenbanken

Accordion

Bootstrap-Accordion (data-bs-toggle="collapse" + data-bs-parent) für aufklappbare Gruppen, z. B. BBPO-Versionen.

Struktur der Version 2021 …

Struktur der Version 2018 …

Seitenaufbau & Breadcrumbs

layouts/base.html liefert das Grundgerüst: den page-header (mit <h2 class="page-title"> aus der Context-Variable page_title) und die page-body. Content-Seiten füllen nur Blöcke — keinen eigenen Seitenkopf bauen.

Blöcke einer Content-Seite:
{% extends "layouts/base.html" %}
{% block title %}…{% endblock %}          {# Browser-Tab #}
{% block page_subtitle %}…{% endblock %}  {# <div class="text-muted mt-1"> unter dem Titel #}
{% block page_actions %}…{% endblock %}   {# rechts oben, meist <div class="btn-list"> #}
{% block content %}…{% endblock %}
{% block styles %}…{% endblock %} · {% block scripts %}…{% endblock %}

Breadcrumbs kommen datengetrieben aus der Context-Liste breadcrumbs ([{label, url}]) und rendern automatisch im Header als breadcrumb breadcrumb-arrows. Statische Variante bei Bedarf:

Tabs

Auf Seitenebene nav nav-tabs + tab-content/tab-pane, umgeschaltet über data-bs-toggle="tab" (client-seitig, z. B. Studierenden-Detail). Für server-nachgeladene Ansichten stattdessen nav-tabs card-header-tabs + HTMX (hx-get#target, aktiver Tab via active_tab). nav-pills wird nicht verwendet.

Inhalt „Übersicht" …
Inhalt „Studienplan" …
Inhalt „Fortschritt" …

Button-Group & Ansicht-Umschalter

btn-group role="group" mit aktivem Zustand über .active (per JS gesetzt, data-value/data-view). Für den Wechsel Kacheln↔Tabelle die Standard-Icons ti-grid-dots / ti-table.

Dropdown-Menüs

Bootstrap-Dropdown (data-bs-toggle="dropdown"). Als Aktions-Menü ein btn-icon mit ti-dots-vertical; Menüpunkte tragen ein führendes Icon mit me-2, Trenner via dropdown-divider, rechtsbündig mit dropdown-menu-end.

Pagination

Tabler pagination mit Chevron-Icons; aktive Seite als page-item active mit <span class="page-link">. Kompakt via pagination-sm, im card-footer rechtsbündig.

Modals / Dialoge

Standard: modal modal-blur fade + modal-dialog modal-dialog-centered, Größe modal-sm/modal-lg/modal-xl, bei langem Inhalt modal-dialog-scrollable. Der Header trägt modal-title + btn-close (Icon-only, kein Label-Icon), die Footer-Buttons tragen wie üblich ein führendes Icon.

Auslöser: data-bs-toggle="modal" data-bs-target="#…"

Für „Wirklich löschen?"-Rückfragen ist die Inline-Bestätigung data-confirm-delete (arm-then-confirm, s. u.) der Standard — kein eigener Dialog nötig.

Alerts & Toasts

Toast = flüchtige Rückmeldung (rechts unten, auto-dismiss). Django- messages werden app-weit automatisch als Toast ausgegeben (layouts/base.html), nicht als Alert-Box; programmatisch window.hdaToast("…") / hdaToast("…","error") (s. Interaktive Bausteine). Alert = persistenter Hinweis im Seitenfluss; Varianten alert-success/-danger/-warning/-info/-secondary, optional alert-dismissible + btn-close, Icon als inline <i class="ti …">.

Tooltips (zwei Systeme)

Je nach Zweck einen der beiden Mechanismen wählen:

Begriffs-Tooltip .hint

CSS-only, für Fachbegriffe im Fließtext: unterstrichenes Wort, Hover zeigt eine dunkle Box. <span class="hint" data-tooltip="…">Begriff</span> (s. Interaktive Bausteine). Kein JS nötig.

Der ECTS-Wert …

Bootstrap-Tooltip

Für Icon-Buttons/Steuerelemente: data-bs-toggle="tooltip" + data-bs-title="…". Wird global in base.html initialisiert (auch nach HTMX-Swaps). Für einfache Fälle genügt das native title="…".

← Hover für Tooltip

Diagramme (ApexCharts)

Visualisierungen (Notentrend, Kohorten) nutzen ApexCharts, global in base.html geladen. In die Marken-Palette einfärben — Primär-Serie h_da-Orange #E98011 (bzw. Anthrazit #231F20); keine Apex-Default-Farben. Der Container ist ein leeres <div id="…">, die Config kommt im {% block scripts %}.

Willkommens-Hero

Die Begrüßungsbox oben auf den Hauptseiten (Dashboard, Study-Monitoring, Studiengänge …): Verlaufsfläche in Markenfarbe, links Icon + Titel + Lead, rechts eine unDraw-Illustration (portal-welcome-art, auf schmalen Breiten ausgeblendet), darunter optional bis zu drei portal-step-Kacheln als Einstiegs-Links. Für Mitarbeitende die kompakte Variante portal-welcome--compact. Demo unter .sg-hero/.sg-step.

Willkommen bei Mitbewerber

Mitbewerber-Analyse für die Informatik-Studiengänge: vergleichbare Angebote anderer Hochschulen erfassen, gegenüberstellen und auswerten.

Hero-Kontext (.hero-context)

App-weit einheitliche „aktuell selektiert / zugeordnet"-Zeile unter dem Lead: .hero-context-label (Icon + Text) + Chips. Chips read-only als <span class="hero-context-chip"> oder navigierbar als <a class="hero-context-chip">; für Pfade Chevron-Trenner .hero-context-sep. Farben laufen über die Marken-Variablen und kippen im Mitarbeitenden-Theme automatisch auf Orange. Genutzt in Dashboard, KI-Assistent, Kohortenanalyse und Study-Monitoring — nicht pro Seite nachbauen.

Zugeordnet: Informatik dual B.Sc. Data Science M.Sc.
Aktuelle Auswahl: h_da FB Maschinenbau

Illustrationen (unDraw)

Spot-Illustrationen von unDraw (MIT-Lizenz, attributionsfrei). Diese Galerie wird automatisch aus dem Ordner erzeugt – neue undraw_*.svg erscheinen hier von selbst.

So bindest du eine neue Illustration ein:

  1. Auf die Markenfarbe umfärben: unDraws Default-Akzent #6c63ff → h_da-Orange #E98011 ersetzen — sed 's/#6c63ff/#E98011/gI' rohdatei.svg > undraw_<slug>_hda_orange.svg.
  2. Unter dem Static-Ordner einer App vendoren (<app>/static/**/img/undraw_*.svg, im Projekt app/static/hda/img/) und den Original-Slug im Dateinamen behalten (Nachvollziehbarkeit, z. B. undraw_interview_hda.svginterview_yz52).
  3. Per {% static 'hda/img/undraw_….svg' %} referenzieren — niemals cdn.undraw.co hotlinken (CDN-Pfad ist cdn.undraw.co/illustration/<slug>.svg, Singular illustration).
All The Data Anthracite
All The Data Anthracite
undraw_all_the_data_hda_anthracite.svg
All The Data Blue
All The Data Blue
undraw_all_the_data_hda_blue.svg
All The Data Green
All The Data Green
undraw_all_the_data_hda_green.svg
All The Data Orange
All The Data Orange
undraw_all_the_data_hda_orange.svg
All The Data Red
All The Data Red
undraw_all_the_data_hda_red.svg
All The Data Violet
All The Data Violet
undraw_all_the_data_hda_violet.svg
All The Data Yellow
All The Data Yellow
undraw_all_the_data_hda_yellow.svg
Calendar Anthracite
Calendar Anthracite
undraw_calendar_hda_anthracite.svg
Calendar Blue
Calendar Blue
undraw_calendar_hda_blue.svg
Calendar Green
Calendar Green
undraw_calendar_hda_green.svg
Calendar Orange
Calendar Orange
undraw_calendar_hda_orange.svg
Calendar Red
Calendar Red
undraw_calendar_hda_red.svg
Calendar Violet
Calendar Violet
undraw_calendar_hda_violet.svg
Calendar Yellow
Calendar Yellow
undraw_calendar_hda_yellow.svg
Educator Anthracite
Educator Anthracite
undraw_educator_hda_anthracite.svg
Educator Blue
Educator Blue
undraw_educator_hda_blue.svg
Educator Green
Educator Green
undraw_educator_hda_green.svg
Educator Orange
Educator Orange
undraw_educator_hda_orange.svg
Educator Red
Educator Red
undraw_educator_hda_red.svg
Educator Violet
Educator Violet
undraw_educator_hda_violet.svg
Educator Yellow
Educator Yellow
undraw_educator_hda_yellow.svg
Interview Anthracite
Interview Anthracite
undraw_interview_hda_anthracite.svg
Interview Blue
Interview Blue
undraw_interview_hda_blue.svg
Interview Green
Interview Green
undraw_interview_hda_green.svg
Interview Orange
Interview Orange
undraw_interview_hda_orange.svg
Interview Red
Interview Red
undraw_interview_hda_red.svg
Interview Violet
Interview Violet
undraw_interview_hda_violet.svg
Interview Yellow
Interview Yellow
undraw_interview_hda_yellow.svg
Online Learning Anthracite
Online Learning Anthracite
undraw_online_learning_hda_anthracite.svg
Online Learning Blue
Online Learning Blue
undraw_online_learning_hda_blue.svg
Online Learning Green
Online Learning Green
undraw_online_learning_hda_green.svg
Online Learning Orange
Online Learning Orange
undraw_online_learning_hda_orange.svg
Online Learning Red
Online Learning Red
undraw_online_learning_hda_red.svg
Online Learning Violet
Online Learning Violet
undraw_online_learning_hda_violet.svg
Online Learning Yellow
Online Learning Yellow
undraw_online_learning_hda_yellow.svg
Respond Orange
Respond Orange
undraw_respond_hda_orange.svg
Scrum Board Anthracite
Scrum Board Anthracite
undraw_scrum_board_hda_anthracite.svg
Scrum Board Blue
Scrum Board Blue
undraw_scrum_board_hda_blue.svg
Scrum Board Green
Scrum Board Green
undraw_scrum_board_hda_green.svg
Scrum Board Orange
Scrum Board Orange
undraw_scrum_board_hda_orange.svg
Scrum Board Red
Scrum Board Red
undraw_scrum_board_hda_red.svg
Scrum Board Violet
Scrum Board Violet
undraw_scrum_board_hda_violet.svg
Scrum Board Yellow
Scrum Board Yellow
undraw_scrum_board_hda_yellow.svg

Interaktive Bausteine (global)

App-weite JS-Utilities aus static/hda/js/, global in layouts/base.html geladen — nicht pro Seite nachbauen.

Copy-to-Clipboard & Toast

Beliebiges Element mit data-copy="…" (optional data-copy-message); Inline-Trigger als btn-copy. Toasts programmatisch: window.hdaToast("…") bzw. hdaToast("…", "error"). Utility: static/hda/js/clipboard.js (self-injiziert sein CSS).

bjoern.baer@h-da.de

Inline-Editing (+ KI)

.inline-edit + data-ie-url/data-ie-field/data-ie-value. Hover zeigt einen orange gestrichelten Indikator; Klick öffnet den Editor (bei viel Text ein Textarea in Originalgröße), Enter/Blur speichert (Endpoint gibt JSON mit HTTP 200 zurück), Esc bricht ab. Beim Hover erscheint am Zeilenende ein KI-Icon zum Umformulieren (Presets Rechtschreibung/Sachlicher/Freundlicher/Du/Sie/Deutsch/Englisch + eigener Prompt) — es postet nach /ai/rewrite/, in diesem Projekt noch nicht implementiert. Für Icon-Felder: data-ie-render="icon" öffnet den Icon-Picker. Utility: static/hda/js/inline-edit.js.

Beispiel (Demo, speichert nichts): Klick mich zum Bearbeiten

Löschen-Bestätigung (arm-then-confirm)

Ruhezustand btn-outline-danger + data-confirm-delete (optional data-confirm-label). 1. Klick füllt zu btn-danger und zeigt die Bestätigung, erst der 2. Klick führt aus; Klick daneben / Esc / Timeout entschärfen. Funktioniert für Links, Form-Submit und eigene JS-Click-Handler (static/hda/js/confirm-delete.js).

Tabellen-Volltextfilter (data-table-filter)

App-weite JS-Utility (static/hda/js/table-filter.js): ein Suchfeld filtert clientseitig die Zeilen einer Tabelle über ihren Text. Opt-in am <input data-table-filter="#tbody">; optional data-filter-count (Element für den Trefferzähler) und data-filter-label. Gefiltert werden Zeilen mit data-row; als Suchtext dient data-search (falls gesetzt, z. B. voller Pfad für hierarchische Listen), sonst der Zeilentext. data-no-match blendet eine „Kein Treffer"-Zeile ein. Läuft auch für per htmx nachgeladene Inhalte. Toolbar-Standard (vgl. Card-Header „Titel + Suchfeld"): input-icon + form-control-sm, Count-Badge, Add-Button rechts als btn-sm — app-weit einheitlich (z. B. alle Administration-Tabs).

StudiengangFachbereich
Informatik B.Sc.FB Informatik
Data Science M.Sc.FB Mathematik und Naturwissenschaften
Mechatronik B.Eng.FB Maschinenbau
Kein Treffer.

E-Mail-Schutz (Anti-Harvesting)

Auf (auch ausgeloggt erreichbaren) Seiten stehen E-Mail-Adressen nicht im Klartext im HTML, sondern nur kodiert (data-eml); static/hda/js/email-protect.js macht daraus beim Laden automatisch einen klickbaren mailto:-Link — ohne dass Nutzer*innen etwas tun. Verwendung: {% load email_protect %} dann Tag {% protect_email "name@example.com" %} (Link) oder Filter {{ mail|obfuscate_email }} für eigene Attribute (im JS via window.hdaDecodeEmail(value) entschlüsseln). Läuft auch nach htmx-Swaps.

Beispiel (Quelltext enthält keinen Klartext, JS macht daraus den Link): E-Mail

Tooltip-Hinweis (.hint)

App-weite CSS-Utility (static/hda/css/hda-theme.css): ein unterstrichener Begriff, der beim Hovern eine gestylte dunkle Box mit Erklärtext zeigt. Markup: <span class="hint" data-tooltip="Erklärung">Begriff</span>. (.org-unit-tooltip ist ein Alt-Alias mit gleicher Optik.)

Beispiel: Der ECTS-Wert ergibt sich aus den bestandenen Modulen.

Drag-&-Drop-Sortierung (Autosave)

Container (z. B. <tbody>) bekommt data-sortable + data-sortable-url (POST-Ziel), jede Zeile data-sortable-id="<pk>" und einen Greifpunkt .sortable-handle mit ti-grip-vertical (nur dort startet das Ziehen). Beim Loslassen wird order[]=<id>… gepostet und hdaToast meldet das Ergebnis. Alle Handler sind delegiert — auch dynamisch (z. B. per AJAX) eingefügte Zeilen sind sofort sortierbar. Utility: static/hda/js/sortable-rows.js.

Erste Zeile
Zweite Zeile
Dritte Zeile

Formular-Elemente


TomSelect – durchsuchbare Selects

Jedes <select class="js-tomselect"> wird von select-widgets.js auto-initialisiert (nach tom-select.complete.min.js) — Tabler-nativ, select2/jQuery projektweit entfernt. Konfiguration rein über Attribute; nichts pro Seite verdrahten. Single-Selects erhalten automatisch ein „×" zum Abwählen (clear_button-Plugin) — sichtbar nur, wenn etwas gewählt ist; Mehrfachauswahl ein remove_button-„×" je Chip.

js-tomselect + data-placeholder (leere erste Option für den Platzhalter); nach der Wahl erscheint das × zum Abwählen
multipleremove_button-Plugin automatisch (× je Eintrag)
data-ts-create erlaubt neue Einträge (data-ts-create="email" nur, wenn der Text ein „@" enthält)

Optionen serverseitig laden statt statisch: data-ts-url zeigt auf einen Endpunkt (Query-Param ?q=), Antwortformat {"results":[{"id","text"}]}. Programmatisch alternativ window.hdaSelectRemoteLoad(url) als TomSelect-load-Funktion.

<select class="js-tomselect"
        data-ts-url="/portal/api/users?q="
        data-placeholder="Person suchen …"></select>

Dual-List – Zuordnen per Verschieben

Zwei-Spalten-Widget für Mehrfachzuordnungen (z. B. Studiengänge → Benutzer): links die verfügbaren, rechts die zugeordneten Einträge; verschoben wird per Doppelklick auf einen Eintrag oder über die Pfeil-Buttons (markierte / alle). Progressive Enhancement über ein <select multiple> — das native Select bleibt versteckt im DOM, sodass der Form-Submit unverändert läuft. Opt-in per data-dual-list; global geladen (hda/js/dual-list.js), nichts pro Seite verdrahten. Für einfache, kurze Mehrfachauswahl bleibt TomSelect (oben) die erste Wahl; Dual-List lohnt bei längeren Listen und wenn die Trennung „verfügbar ↔ zugeordnet" explizit sichtbar sein soll.

Doppelklick verschiebt sofort; Einfachklick markiert, dann Pfeil-Buttons. Suchfelder je Spalte per Default (data-dual-list-search="0" blendet sie aus).
<select class="form-select" name="programs" multiple data-dual-list
        data-dual-list-available="Verfügbar" data-dual-list-selected="Zugeordnet">
    <option value="if-b" selected>Informatik B.Sc.</option>
    …
</select>