Components

Vacancy

One open position in a register of them. Not a card: a block of type standing on a hairline, with the facts of the job as a description list under it. It is the same drawing as the search result — literally the same rules, naming both — plus the one part a result has no use for.

Default

A mono line naming the team, a linked title, one or two sentences, and four facts. The register draws its own top edge and every entry closes with its own rule including the last, so the list is bounded at both ends.

  • Plattform

    Data Engineer (m/w/d)

    Sie bauen die Strecken, auf denen Anlagendaten ankommen: Anbindung, Modellierung, Qualitätssicherung. Ohne dieses Fundament ist jede Auswertung darüber geraten.

    Standort
    Konstanz, hybrid
    Anstellung
    Festanstellung
    Umfang
    Voll- oder Teilzeit
    Start
    ab sofort
  • Produkt

    Werkstudent Frontend (m/w/d)

    Sie bauen an der Oberfläche mit, auf der Betreiber ihre Anlagen lesen. HTML, CSS und so viel JavaScript wie nötig — bei uns ist das erfahrungsgemäß wenig.

    Standort
    Konstanz
    Anstellung
    Werkstudium
    Umfang
    16–20 Std./Woche
    Start
    ab sofort
<ul class="cf-vacancies" role="list">
  <li class="cf-vacancy">
    <p class="cf-vacancy__meta"><span>Plattform</span></p>
    <h3 class="cf-vacancy__title">
      <a class="cf-vacancy__link" href="/karriere/data-engineer">Data Engineer (m/w/d)</a>
    </h3>
    <p class="cf-vacancy__excerpt">Sie bauen die Strecken, auf denen…</p>
    <dl class="cf-vacancy__facts">
      <div class="cf-vacancy__fact">
        <dt class="cf-vacancy__term">Standort</dt>
        <dd class="cf-vacancy__value">Konstanz, hybrid</dd>
      </div>
      <div class="cf-vacancy__fact">…</div>
    </dl>
  </li>
</ul>
One drawing, two registers. Every rule this component uses for the row, the sheen, the mono line, the title, the link and the excerpt is the search result's rule with .cf-vacancy… added to its selector list — the same move .cf-prose table makes alongside .cf-table. A job opening and a search hit are the same object at the level the drawing works on: a mono line, a linked title, a couple of sentences on a hairline. Writing that twice would be two things to keep in step, and things that have to be kept in step drift. Only .cf-vacancy__facts is declared on its own.

The title picture

An opening names one picture that stands for it — bild: in its file, the Titelbild property in the Stellen register — and the row is drawn from it: .cf-vacancy--image on the entry, an <img> with .cf-vacancy__image as its first child, and everything the picture is not moved into a <div class="cf-vacancy__body"> beside it. scripts/build-jobs.py writes all three from the same record the sync filled, so the record, the file and the drawing cannot drift apart.

  • Plattform

    Data Engineer (m/w/d)

    Sie bauen die Strecken, auf denen Anlagendaten ankommen: Anbindung, Modellierung, Qualitätssicherung.

    Standort
    Konstanz, hybrid

The facts

Four name/value pairs — Standort, Anstellung, Umfang, Start — which is what <dl> is for and what nothing else in HTML is. They are the questions every candidate asks before reading a word of the description, so they are answered on the register rather than one click further in.

The drawing is the contact list's: a mono term with its value under it. What changes is the axis. Four short facts laid down a column would be four rows under every opening and would make the register three times as long as its content; laid along the row they read as a strip, and they wrap onto a second line on a phone without any rule of their own.

Column gap is --space-8 against a row gap of --space-3, and the asymmetry is the whole legibility of the block: a pair has to read as a pair before the row reads as a row, so the air between two facts must beat the air inside one. There are no rules between them — a line there would be neither an edge nor a division nor a label rule, which is the test in where a line may go. The register's own hairline is the only line in the component.

Keep it to four. A fifth fact is a fact for the job page, and a register whose facts wrap to three lines has stopped being a register.

When nothing is open

A company that is not hiring is the normal state, not an error, so the register is replaced rather than rendered empty — .cf-error--inline, at 200, exactly as the news overview does for a filter that matches nothing. → Error & Empty State

0 Stellen

Gerade ist nichts ausgeschrieben.

Wir stellen ein, wenn Arbeit da ist, nicht auf Vorrat. Eine Initiativbewerbung lesen wir trotzdem — und antworten auch dann.

Note which button that is. On the page itself the initiative link is .cf-btn--primary and is the screen's one lime moment; in the empty state it drops to --outline, because .cf-error--inline is already the loudest thing on an otherwise empty page and two claims on one screen is one too many.

Anatomy

ClassElementRequiredDoes
.cf-vacanciesulyesThe register. role="list" because list-style: none drops list semantics in Safari. Draws the top edge, or gives it up to a .cf-section-header--flush above it.
.cf-vacancyliyesOne opening. Column stack, --space-6 of air above and below, its own bottom hairline, and --sheen-panel travelling across it on hover or focus-within.
.cf-vacancy--imageli modifierwith pictureRotation to a row: the square on the left, everything else in a .cf-vacancy__body beside it, --space-4 between the two.
.cf-vacancy__imageimgwith pictureThe square. --space-24 across, aspect-ratio: 1/1, object-fit: cover, decorative alt=""--space-16 below 34 rem.
.cf-vacancy__bodydivwith pictureEverything the picture is not. Only on an image row, so the column stack from .cf-vacancy moves in here rather than being written twice.
.cf-vacancy__metapnoMono 11 px uppercase, --text-secondary. The team, and nothing that is already a fact below. A row of <span>s, so a second item needs no separator.
.cf-vacancy__titleh3yesThe role. Display face, --text-h4. A heading level, not a class — pick the level the page's outline needs.
.cf-vacancy__linkayesThe link is the title and only the title. It underlines on hover and on :focus-visible; the row lights under it either way.
.cf-vacancy__excerptpnoOne or two sentences, capped at --measure-prose. What the job is, not what the company is.
.cf-vacancy__factsdlnoThe wrapping strip of pairs. --space-3 row gap, --space-8 column gap.
.cf-vacancy__factdivwith factsOne pair. The wrapper is required by <dl> if term and value are to stay together when the strip wraps.
.cf-vacancy__termdtwith factsMono 11 px uppercase, --text-secondary. The contact list's term at label scale.
.cf-vacancy__valueddwith factsBody face, --text-md, full ink, --space-1 under its term.

Accessibility

Before launch

Rules

Do

  • Put the register under a .cf-section-header--flush and let one hairline do both jobs.
  • Answer the four facts every candidate asks before they ask them.
  • Write the title so it stands alone in a list of links.
  • Replace the whole register with .cf-error--inline when nothing is open.
  • Keep the excerpt to what the job is. The company is the section above.
  • Draw the square only when the opening names one picture. An entry without one is the row the register has always drawn.

Don't

  • Don't box it. This is a register, not a grid of cards — there is no plate and no corner.
  • Don't add a rule between the facts, or down the side of an entry. The row hairline is the whole drawing.
  • Don't spend lime on an entry. The page has one lime moment and it is the initiative button under the list.
  • Don't stretch a link over the entry. It costs the reader the ability to select the facts.
  • Don't put a fifth or sixth fact in the strip. That is content for the job page.
  • Don't build a filter into the component. Narrowing a list is a server's work, the same as it is for the news overview.
  • Don't link the picture or give it an alt. It is decoration for scanning a list; the title is the link and the block beside it carries the content.
  • Don't draw a placeholder where a picture is missing. A row of grey squares reads as a fault, and this component does not reserve a hole.