Skip to content
Navigation

Logo

The Ströer wordmark. Inline SVG, so it stays sharp at any size and takes its colour from the surrounding text.

Added 14 October 2026

Component, or a file in public/?

Always the component. The mark exists in this repo six times over — three <img src> call sites in ppv-self-booking, one in national-planner’s navbar, and two apps rendering a PNG, which is why the logo looks soft there on a HiDPI screen.

An <img> pointing at an SVG is sharp, but it can never take a colour: the file’s own fill wins. Inlining the SVG is what makes currentColor — and therefore dark mode — possible at all. That is the whole reason this is a component and not another copied asset.

The artwork, the size ladder and the naming rule live once, in lib/logo.ts, and both framework components import them.

Colour: inherit by default, brand-fixed on purpose

The mark paints with currentColor, so it follows the text colour of wherever you put it. That is the default because it is the safe one.

  • Inherit (<Logo />) — inside a footer, a link, a card, anything that already has a sensible text colour. Works in both themes without a second thought.
  • text-brand-fixed dark:text-brand-fixed-foreground — the brand pair, and the right choice on a brand surface such as an app header. --brand-fixed is fixed identity with no .dark override, so on its own it renders navy on a dark surface at ~1.2:1 — invisible. Always give it the dark half.
  • text-brand-fixed-foreground — on a bg-brand-fixed surface, i.e. the mark sitting on brand navy.
  • text-muted-foreground — a de-emphasised mark: partner lists, footnotes, “powered by” rows. Light surfaces only: in dark that token is a warm olive (hue 100), which tints the wordmark khaki. Prefer text-foreground/60 if you need de-emphasis in both themes.

A logotype is exempt from the WCAG contrast minimums (1.4.3, 1.4.11) and is the sanctioned exception to 1.4.5 Images of Text — which is what licenses this component to be an image of text at all. That is why text-muted-foreground is allowed here although it sits on the contrast gate’s KNOWN list. Aim for 3:1 anyway; an unreadable mark is still a bad mark.

Size: a preset, or an exact box

The wordmark’s ratio (~4.53:1) comes from the artwork, so one dimension is always enough.

<Logo size="sm" />   // 20px — headers, dense bars
<Logo size="md" />   // 24px — default
<Logo size="lg" />   // 32px — sign-in, empty states
<Logo size="xl" />   // 40px — print, hero
<Logo height={28} /> // exact height → 127×28
<dl-logo size="sm" />
<dl-logo [height]="28" />

Minimum size: 20px tall (sm), which is why the ladder starts there rather than at 16. The Ö diaeresis occupies about 3 of the artwork’s 42 viewBox units — roughly 1.1px at 16px tall — so on a 1× display the two dots merge and the mark reads “Stroer”. Presets also exist so the mark is not 16px in one app and 28px in the next. Nothing enforces the floor for an explicit width/height; treat 20px as the rule anyway.

Clear space: at least 1× the mark’s height on every side, and nothing but background inside it. The artwork itself carries almost no padding (1.2 viewBox units ≈ 0.6px at sm), so the space has to come from your layout.

An off-scale height: <Logo className="h-7" /> or <Logo className="h-4 md:h-6" /> — className merges, so your height replaces the preset’s while w-auto survives and the ratio still resolves. In Angular the class goes on the host: <dl-logo class="h-7">. This is the better escape hatch, and the responsive one.

width/height are for non-CSS contexts that need the attributes — a PDF header, a fixed-size slot. Passing either drops the preset, and prefer passing one:

  • Both dimensions letterbox inside your box; preserveAspectRatio is left at its default, so the letters are never stretched.
  • A px box does not scale with the user’s text size, while the rem-based presets do. Use it only where the surrounding box is fixed too.
  • The two frameworks disagree about precedence here, the one place they do: React passes presentation attributes, so a class beats them; Angular applies inline styles, so they beat a class. Do not combine a class with an explicit dimension — pick one mechanism.

Named, or decorative

The logo is an image with a name, so it needs one — unless the name is already there. [a11y]

<Logo />            // role="img" + aria-label
<Logo decorative /> // aria-hidden, silent
<dl-logo />
<dl-logo decorative />
  • Named — the mark is the only thing that says whose product this is: a header whose other text is a bare product name (as in the showcase, whose header link reads “Ströer design library”), a sign-in card, an email header.
  • decorative — the neighbouring text already contains the brand name (“Ströer Core”, “Ströer Self-Booking”), or the mark sits alone in a link that carries its own name. [a11y]
  • label — only if the brand name itself differs in context. It is a proper noun; do not translate it. A blank label is treated as decorative rather than emitted as an empty name, because role="img" with no name is a 1.1.1 failure.
  • role, aria-label and aria-hidden are not accepted as props. decorative and label are the only naming levers, on both frameworks. [a11y]
<a href="/"><Logo /> <span>design library</span></a>
<a href="/" aria-label="Ströer — home"><Logo decorative /></a>

A mark alone in a link with no label leaves the link named only “Ströer” — it identifies the brand but not the destination (2.4.4). A decorative mark alone in an unlabelled link leaves the link with no accessible name at all: a 2.4.4 and 4.1.2 failure. [a11y]

In a header

HeaderLogo / dl-header-logo is the slot, not the mark — it only positions what you put inside:

<Header>
  <HeaderLogo>
    <Logo size="sm" />
  </HeaderLogo>
  <HeaderActions>…</HeaderActions>
</Header>
<dl-header>
  <dl-header-logo>
    <dl-logo size="sm" />
  </dl-header-logo>
  <dl-header-actions>…</dl-header-actions>
</dl-header>

In a lockup with type, align the mark’s box bottom to the text baseline — that is what items-baseline on the row gives you. Do not add self-center: it cancels the baseline alignment and leaves the mark hanging on the label’s descender line.

Never

  • A copied stroeer-logo.svg in an app’s public/, or an <img> pointing at one — that is what this component replaces.
  • A raster logo. A PNG cannot stay sharp across zoom levels and screen densities.
  • text-brand-fixed without its dark: half — the token has no dark override, so the mark vanishes.
  • Recolouring by anything other than a text-* token: no fill-[#08204a], no inline style.
  • Hand-written role/aria-* on the mark — use decorative / label. [a11y]
  • Both width and height set “to be safe” — you get letterboxing you did not ask for. Pass one.
  • Below 20px tall, or with less than 1× clear space.
  • A backing plate, gradient or shadow behind the mark; rotating, outlining, cropping or stretching it. If a layout needs that, the layout is wrong.

Open questions

  • The artwork is vendored from an app’s public/ folder, not from a brand kit. Now that it ships here it is the de-facto source of truth for every app, so it deserves a check against the official asset — as do the 20px floor and the 1× clear space, which are this library’s reading and not a brand-guide citation. The floor in particular rests on a rasterisation judgement that wants confirming on a real 1× monitor.
  • The product lockups are missing. “STRÖER | core” (product-access-management-ui) is a two-colour mark, so currentColor cannot paint it, and it exists only as a PNG today. That app keeps its raster copy until someone supplies an SVG and decides how the second colour is themed — most likely a second component rather than a variant of this one.
  • /color — the brand tiers, and why brand-fixed does not adapt while brand-interactive does.
  • docs/components/header.md — the header slots the mark goes into.