Skip to content

Latest commit

 

History

History
346 lines (254 loc) · 15.7 KB

File metadata and controls

346 lines (254 loc) · 15.7 KB

CLARIN-DK Frontend Customization Guide

This document maps how the DSpace Angular frontend is composed, identifies the Czech/LINDAT-specific parts inherited from the upstream ufal/dspace-angular fork, and tracks what has already been adapted for repository.clarin.dk.


Local Development Setup

1. Start the backend (Docker)

Run from the project root — this is required so Docker Compose picks up .env (which sets INSTANCE, ports, and the subnet). Running from docker/ loses the env file and causes wrong ports and subnet conflicts.

docker compose --env-file .env -p d7 \
  -f docker/docker-compose-rest.yml \
  -f docker/docker-compose.dev.yml \
  up -d

Starts dspace0 (REST/Tomcat on :8080), dspacedb0 (PostgreSQL), dspacesolr0 (Solr).

2. Install frontend dependencies (first time or after a branch switch)

npm install --legacy-peer-deps

--legacy-peer-deps is required due to a peer-dep conflict with @kolkov/ngx-gallery.

3. Start the frontend dev server

NODE_ENV=development npx ts-node --project ./tsconfig.ts-node.json scripts/serve.ts

The port is read from appConfig.ui.port (default 4000 in default-app-config.ts), but if config.yml has no ui: section the value may not resolve correctly and Angular will fall back to its own default of 4200. Check the startup log — it prints the actual URL the server is listening on.

If yarn is in your PATH you can use yarn start:dev instead — it wraps the same scripts/serve.ts in nodemon so the server process also restarts when config.yml changes.

Notes

  • Theme: config/config.yml contains themes: [{name: custom}] which activates the CLARIN-DK theme. This is committed — do not remove it.
  • Config changes: Edits to config/config.yml are not hot-reloaded; restart the dev server to pick them up.
  • yarn.lock: Do not commit changes to yarn.lock generated by npm install. If you need to add a dependency use yarn add to keep the lockfile consistent.

CSS Architecture

The custom theme uses three SCSS files that each serve a distinct purpose. The import order in theme.scss is what determines which file belongs where — not preference.

File What goes here Why
_theme_sass_variable_overrides.scss Bootstrap/DSpace SCSS compile-time variables and theme SCSS palette variables ($flexoki-*, $body-color, etc.) Imported before Bootstrap; variables in scope for all files imported later
_theme_css_variable_overrides.scss CSS custom properties that must be accessible at runtime via var() — currently --bs-*, --ds-*, and --lt-clarin-* Imported after Bootstrap; values derived from SCSS vars via #{} interpolation
_global-styles.scss Component overrides for upstream/base components, selectors, !important fixes Imported last; uses $flexoki-* and $dk-* SCSS variables directly

Flexoki colour palette

$flexoki-* SCSS variables are the single source of truth for the palette — hex values live only in _theme_sass_variable_overrides.scss. There are no --flexoki-* CSS custom properties; the theme is static and does not need runtime colour access.

The --lt-clarin-* CSS custom properties in _theme_css_variable_overrides.scss are derived from $flexoki-* via #{} interpolation and are CSS custom properties because _clarin-styles.scss references them via var(). Do not add --flexoki-* custom properties — if a new component needs a Flexoki colour, use the SCSS variable directly.

The $flexoki-cycle SCSS map in _global-styles.scss uses the SCSS variables to cycle through eight Flexoki colours for cards, badges, and skeleton loaders.

When Bootstrap variable duplication is unavoidable

Some Bootstrap SCSS variables feed into many derived values at compile time. $body-color is the main example — Bootstrap derives input colours, component colours, and other values from it. For these, the value lives in two places:

  • $body-color in _theme_sass_variable_overrides.scss — controls Bootstrap's compiled output
  • --bs-body-color in _theme_css_variable_overrides.scss — the runtime CSS variable read by components

Both have a comment pointing to each other. When you change one, change the other.

Overriding component styles

Components we own (anything under src/themes/custom/app/) — put styles in the component's own .scss file. Two patterns depending on whether you need base styles too:

  • Replace the base SCSS entirely — point styleUrls only at the custom file. Write all styles from scratch (see search-navbar.component.scss). No conflicts, no !important.
  • Extend the base SCSS — include the base file first, then the custom file: styleUrls: ['../../../../app/foo/foo.component.scss', './foo.component.scss']. If the base defines styles inside :host {}, those compile to [_nghost][_ngcontent] giving specificity (0,3,0), and cascade order across two styleUrls entries is not guaranteed. Use !important in the custom file and add a comment explaining why (see home-page.component.scss).

Upstream and base-theme components — override from _global-styles.scss. Angular scopes component styles with [_ngcontent-xxx] giving specificity (0,2,0). Beat it with a selector at (0,2,1) or higher:

ds-some-component .card .target-class { ... }  // (0,2,1) — beats (0,2,0)

If the component uses !important internally (common in the upstream CLARIN styles), match it with !important in _global-styles.scss and add a comment explaining why.


Theme and Component Override System

Components resolve in a three-tier cascade at runtime:

src/themes/custom/   →  src/themes/dspace/   →  src/app/   (base)
   (highest priority)                            (fallback)

Each themed component uses a ThemedComponent<T> wrapper (e.g. themed-footer.component.ts) that dynamically imports the component from the highest-priority theme directory that provides it. If a theme doesn't have an override, the next tier is tried, falling back to the base src/app/ version.

The three CSS bundles (base-theme, dspace-theme, custom-theme) load in that order; later bundles override earlier ones via normal CSS cascade.

Note: src/themes/eager-themes.module.ts currently has the custom eager theme module commented out. This means custom-theme components lazy-load on first access rather than being pre-loaded, which can cause a brief flicker. Consider uncommenting when the custom theme is stable.


How Templates and Translations Work Together

There are no per-language template variants anywhere in the codebase. The entire app uses a single set of templates. Translatable strings use the | translate pipe (e.g. {{'navbar.repository' | translate}}); everything else is hardcoded directly into the template HTML. This means two distinct and independent kinds of work when adapting for a new institution or language:

  • Translation file work — editing .json5 files. Affects all the generic DSpace surfaces: forms, labels, menus, search, item pages, admin panels, and the clarin-* modules. No code changes needed; the pipe does the rest.
  • Template/component work — editing .html and .ts files. Required for anything hardcoded: branding, institution links, images, routing logic. This is independent of translation files.

Translation file layers

Two files are loaded per language at runtime and merged — overlay keys win over base keys:

  • src/assets/i18n/{lang}.json5 — the base catalogue. Webpack converts it to assets/i18n/{lang}.json at build time. en.json5 is the upstream DSpace file — do not add CLARIN-DK keys to it. da.json5 was created by us (no upstream equivalent exists) and serves as the Danish fallback baseline; it should contain only generic DSpace translations, not CLARIN-DK overrides.
  • src/themes/custom/assets/i18n/{lang}.json5 — CLARIN-DK overrides only. Webpack converts this to assets/custom/i18n/{lang}.json at build time. All CLARIN-DK additions and changes belong here.

The merge happens at runtime inside TranslateBrowserLoader (two parallel HTTP fetches + Object.assign) and TranslateServerLoader (two readFileSync calls + Object.assign). In SSR mode the server merges once and stores the result in TransferState; the browser reads it from there with no extra HTTP request.

There is no build-time merge step and no risk of accidentally committing merged base files. git status stays clean.

Language-specific static HTML pages

A small set of pages (about, license terms) are served as pre-rendered static HTML rather than Angular components. These live in src/static-files/, with a language-specific subdirectory for each non-default locale:

src/static-files/          ← English defaults
src/static-files/cs/       ← Czech overrides (about.html, license-*.html)

HtmlContentService.getHtmlContentByPathAndLocale() selects the right file based on the active language, falling back to the English default. If Danish static pages are needed in future, a src/static-files/da/ directory following the same pattern is the mechanism.


Component Map

Each entry notes what is controlled by translation keys and what is hardcoded, so it is clear whether a given change needs a .json5 edit, a template edit, or both.

Navigation / Header

File What it is i18n'd Hardcoded Status
src/themes/custom/app/header/header.component.html Main top nav bar Nav description (aria labels) CLARIN.dk brand label, DSpace logo image, placeholder for main nav links Done
src/themes/custom/app/shared/lang-switch/lang-switch.component.ts Language switcher Flag alt text, aria labels Flag code mapping (engb, dadk) Done
src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts Login/logout Login and logout labels CLARIN Discovery Service URL construction Done

Dead code in base component (src/app/header/header.component.ts): getLangCodeIfCzech() and translateSlug() are inherited by the custom HeaderComponent but never called — the custom template does not use them. They can be removed from the base file when convenient.

Footer

File What it is i18n'd Hardcoded Status
src/themes/custom/app/footer/footer.component.html Full footer All section headings and link labels CLARIN ERIC and NorS hrefs; info.clarin.dk hrefs; GitHub issues link Done

Home Page

File What it is i18n'd Hardcoded Status
src/themes/custom/app/home-page/home-page.component.html Hero section and page layout Section headings and search labels None — logos and LINDAT-specific content removed Done

Navigation Menu Items

Menu items for the public navigation are registered in src/app/menu.resolver.ts:

  • Communities & Collections — hidden (visible: false); single-collection setup makes it redundant.
  • Browse — visible; sections come from the DSpace browse configuration.
  • CLARIN.dk — external link to https://clarin.dk added as a top-level nav item.
  • Statistics — removed from the home page route (home-page-routing.module.ts); the site_stats entry in menu.resolver.ts handles this without auth-gating.

AAI / Shibboleth

The DiscoJuice/AAI inline popup has been removed and replaced with a redirect to the CLARIN Discovery Service. Login is handled entirely by AuthNavMenuComponent.redirectToDiscovery() in src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts. See SHIBBOLETH.md for the full flow.

CLARIN-Specific Modules (src/app/clarin-*)

Generic CLARIN infrastructure: license management, bitstream authorization, citation, Matomo analytics, etc. These are fully i18n-aware — all user-facing strings use translation keys, and TypeScript code uses TranslateService for dynamic messages (notifications, errors). Danish is now active and these modules work automatically with da.json5.

One remaining exception: clarin-license-info.component.html contains an isCsLocale() check that switches CSS layout classes. See Czech/LINDAT Remnants.


Czech/LINDAT Remnants to Clean Up

Dead code — safe to remove

The custom header template is now in place. The following functions in src/app/header/header.component.ts are inherited but never called:

  • getLangCodeIfCzech() — returns 'cs' or empty string, used to build LINDAT URLs.
  • translateSlug() — maps English nav slugs to Czech equivalents.

Needs investigation before removal

  • isCsLocale() in src/app/item-page/clarin-license-info/clarin-license-info.component.ts — toggles d-inline-flex on the divs wrapping a license label. This may be a genuine layout fix for how long translated strings wrap rather than anything Czech-specific. Check whether the layout issue reproduces in other languages before removing the check.

Inert, no action needed now

  • src/static-files/cs/ — Czech versions of license and about pages. Inert while Czech is disabled in prod config. Leave in place; if Danish static pages are needed later, add a src/static-files/da/ directory following the same pattern.

i18n / Language Configuration

The language list is defined in src/config/default-app-config.ts (all 25 languages active by default) and overridden at runtime by docker/config.prod.yml, which currently activates English and Danish:

languages:
  - code: en
    label: English
    active: true
  - code: da
    label: Dansk
    active: true
  - code: cs
    label: Ceština
    active: false

src/assets/i18n/da.json5 exists as the Danish baseline (English values throughout — no upstream Danish translation exists for DSpace 7). CLARIN-DK-specific Danish translations (header nav, footer sections and links, home hero) live in the overlay at src/themes/custom/assets/i18n/da.json5 and are merged in at runtime. The remaining ~6,200 DSpace interface keys fall back to English until translated.


Production Config (docker/config.prod.yml)

Already configured for CLARIN-DK:

  • Host/URLs point to repository.clarin.dk
  • SSL on port 443
  • REST namespace /server
  • English and Danish active; Czech disabled

What's Done / What's Left

Area Status Notes
Production URLs & ports ✓ Done
Language config (disable cs, enable da) ✓ Done
AAI / Shibboleth login ✓ Done Replaced DiscoJuice with CLARIN Discovery Service redirect
Auth plugin sequence (local.cfg) ✓ Done
Header / top nav ✓ Done Custom template with CLARIN.dk branding
Footer ✓ Done Three-column layout; all text i18n'd
Home page hero ✓ Done Stripped LINDAT/UFAL content
Language switcher ✓ Done Flag mapping (en→gb, da→dk); custom template
Danish translations (da.json5) ✓ Done Baseline file created; CLARIN-DK keys translated in overlay
i18n runtime merge ✓ Done TranslateBrowserLoader + TranslateServerLoader merge base + overlay
CSS colour palette ✓ Done $flexoki-* SCSS vars as single source of truth; no duplicated hex
getLangCodeIfCzech() / translateSlug() TODO Remove dead code from src/app/header/header.component.ts
isCsLocale() layout check TODO Investigate in clarin-license-info before removing
Danish static pages TODO Create static-files/da/ if needed
Eager theme module TODO Uncomment custom theme in eager-themes.module.ts when stable