Components

Article & Prose

The reading page the blog grid has been linking to. Two parts that are used separately as often as together: .cf-article is the furniture around a piece of writing — the index, the byline, the tail — and .cf-prose is the writing itself, set by element rather than by class.

The page

Index on the left, text on the right, both inside the normal container. The index is sticky below the navigation once there are two columns; below that width it stays where it is in the source and becomes a block above the text.

This demo is narrower than the real page, and below 1440 px that shows. .cf-article splits at 60 rem, measured against its container. On a shipping page that container is .container; here it is the demo frame, which at a 1280 px viewport is 846 px against the page's 1139 px of content. So the demo below is a single column at 1280 while the pattern page is already two — widen the window past 1440 px to see the layout this component actually ships in.

Widening is not monotonic, though: the documentation sidebar appears at 901 px and takes 319 px out of this column in one pixel of viewport, so the frame is narrower between 901 and roughly 1220 than it is at 900. If the demo looks wrong in that band, that is the chrome around it, not the component. The frame is not padded out to hide any of this: a demo that lies about its width documents a component nobody has.

Ein digitaler Zwilling ist keine 3D-Animation einer Anlage. Er ist ein Datenmodell, das mit der Anlage mitläuft.

Was ein Zwilling wirklich ist

In Ausschreibungen steht meistens „digitaler Zwilling“, gemeint ist aber eines von drei sehr verschiedenen Dingen: ein Geometriemodell, eine Simulation, oder ein laufendes Abbild des Betriebszustands. Nur das dritte verdient den Namen, und der Unterschied ist keine Wortklauberei, sondern eine Frage der Architektur.

Das Datenfundament

Bevor irgendetwas simuliert wird, muss die Telemetrie stimmen:

  • Zeitstempel ohne Zeitzone. Zwei Steuerungen, zwei Auslegungen von timestamp.
  • Signale ohne Einheit. Ein Wert von 3,2 ist erst dann eine Messung, wenn irgendwo steht, wovon.
  • Lücken, die als Nullen ankommen. Der teuerste Fehler, weil er wie ein Messwert aussieht.

Faustregel

Wenn sich der Zustand einer Anlage nicht aus dem Rohdatenstrom rekonstruieren lässt, rekonstruiert ihn auch kein Zwilling.

Wir bauen Code statt PowerPoints.
— Aus unseren Werten

Fazit

  1. Erst messen, dann modellieren.
  2. Den Zwilling versionieren wie Code.
  3. Eine Kennzahl, die weh tut.
Alle Beiträge
<article>
  <header class="cf-page-header"> … <!-- title, rule, mono meta --> </header>

  <div class="container">
    <div class="cf-article">

      <div class="cf-article__rail">
        <nav aria-labelledby="toc">
          <p class="cf-article__toc-title" id="toc">Inhalt</p>
          <ol class="cf-article__toc">
            <li><a href="#datenfundament">Das Datenfundament</a></li>
          </ol>
        </nav>
      </div>

      <div class="cf-article__body">
        <div class="cf-article__byline">
          <img class="cf-article__avatar" src="…" alt="">
          <div>
            <p class="cf-person__name">Daniel Tremer</p>
            <p class="cf-person__role">CEO</p>
          </div>
        </div>

        <div class="cf-prose"> … </div>

        <div class="cf-article__tail"> … </div>
      </div>

    </div>
  </div>
</article>

Prose, element by element

Everything below is bare HTML inside a single .cf-prose wrapper. No element in it carries a class, because article bodies come out of an editor and an editor does not add classes. Anything that needs a class per paragraph is a system that gets bypassed the first time someone is in a hurry.

Heading 2 opens on a hairline

Body copy is 16 px here, one step above the system's 14 px. Running text that is read for minutes at a time gets the larger size; copy that supports a layout does not. A link inside prose is underlined at all times.

Heading 3 has no rule

The measure is one grid track shared by every text element, so a figure can run the full width of the column while every line of type stays inside one 66 ch band.

Heading 4 is a mono label

  • An unordered marker is a 6 px contour square, never a filled dot.
  • It hangs outside the column so the left edge of the copy stays straight.
  1. An ordered marker is a mono ordinal, zero-padded.
  2. Numbers are tabular, so the column does not shift at ten.
A figure runs the full column width. The caption is mono, under a lighter rule.

Anmerkung

A note is sunken, not outlined, and it is opened by its tag rather than marked by a bar beside it. It reads as a step back from the page rather than as one more box on it.

Wir managen Lösungen, nicht ausschließlich Projekte.
— Aus unseren Werten, Über uns
Ruled rows, no vertical lines, tabular numbers
BetriebsartVersatzGeeignet für
Batch1–24 hBerichte, Abrechnung
Micro-Batch1–5 minZustandsüberwachung
Streaming< 1 sRegelung, Netzstabilität
SELECT asset_id, avg(value)
FROM   telemetry
WHERE  ts >= now() - interval '15 minutes'

A rule is the finest of the four sanctioned dashes — a break in the argument, not a divider between sections.

<div class="cf-prose">
  <p class="cf-article__lead">Der Einstieg in den Text.</p>

  <h2 id="datenfundament">Das Datenfundament</h2>
  <p>Fließtext mit einem <a href="/ueber-uns">Link</a>.</p>
  <ul><li>Ein Punkt.</li></ul>

  <figure>
    <svg role="img" aria-label="…">…</svg>
    <figcaption>Bildunterschrift.</figcaption>
  </figure>

  <!-- the source is NOT part of the quote, so it sits outside it -->
  <figure class="cf-quote">
    <blockquote cite="https://…">Wir bauen Code statt PowerPoints.</blockquote>
    <figcaption>— Aus unseren Werten</figcaption>
  </figure>

  <!-- the tag names the aside and carries its rule; one word, plain text -->
  <div class="cf-prose__note">
    <p class="cf-prose__note-tag">Faustregel</p>
    <p>…</p>
  </div>

  <!-- the scroll box is focusable and named by the caption — see Components · Table -->
  <div class="cf-prose__table-scroll" tabindex="0" role="region"
       aria-labelledby="versatz-caption">
    <table><caption id="versatz-caption">…</caption>…</table>
  </div>
</div>

Anatomy

ClassElementDoes
.cf-articledivTwo-column grid: rail and body. One column until the container reaches 60 rem.
.cf-article__raildivThe index. Sticky below the nav in the two-column state.
.cf-article__toc-titlepMono label on a hairline. Names the <nav> via aria-labelledby.
.cf-article__tocolNumbered index, counters zero-padded to two digits.
.cf-article__bodydivByline, prose, tail.
.cf-article__bylinedivRound avatar plus .cf-person__name / __role.
.cf-article__leadpThe standfirst. One size up, one step back in colour.
.cf-article__taildivTags left, the way back right, on a full rule.
.cf-article__taga / liMono chip, 1 px contour, square.
.cf-prosedivRunning text. Styles bare elements; sets 16 px and the 66 ch measure as a grid track.
.cf-prose__notedivAn aside inside the argument. Sunken surface, no contour.
.cf-prose__note-tagpOpens the aside: mono label, hairline running out of it to the far edge. One word, plain text.
.cf-prose__table-scrolldivWrapper that lets a wide table scroll instead of pushing the page. Same drawing as .cf-table__scroll, and it needs the same three attributes to be keyboard-operable. → Components · Table
.cf-quotefigureQuote plus attribution. Usable outside .cf-prose.
.cf-figure--inlinefigureKeeps a figure inside the reading column instead of the full width.

Size and measure

Two numbers in this component are decisions rather than defaults, and both are worth knowing before they get changed.

DecisionValueWhy
Prose size 16 px (--text-lg) The system's body copy is 14 px, which is right for copy that supports a layout. Text read for minutes at a time steps up. The system already does this — .cf-value-row__body goes to 16 px once it has the room — so prose only makes the move explicit.
Measure 53 ch (--measure-prose) The readable band for running text is 45–90 characters. ch is the width of the font's zero, and Geist's zero is a wide 0.6875 em — so 53 ch holds 77–80 measured characters of German body text, not 53. The token was 66 ch, which drew 90 characters at a 768 viewport and 100 at 1280: past the band. --measure stays at 56 ch for everything that is not an article.
The measure is a grid track, not a max-width per element. ch is font-relative, so 53ch declared on an h2 resolves against that heading's 32 px and lands at 1166 px, while the paragraph beneath it resolves the same token at 16 px and lands at 583 px. Every element would get its own column and the headings would overhang the text they head. Declaring the track once on .cf-prose resolves 53ch once — at the prose's own size — and every child sits in the same 583 px column. The second track takes the remainder, which is what lets a figure span both and step outside the type. A figure that should stay in the column gets .cf-figure--inline.

Accessibility

The sticky rail needs no script and gets none. There is no scroll-spy and no aria-current that updates as you read: it would need JavaScript, and the system ships exactly one script. An index that does not follow along is still an index.

Rules

Do

  • Give every h2 an id and put it in the index — five to eight entries.
  • Let figures run the full column width; keep type in the measure.
  • Use the note for an aside the reader may skip, and keep it to three or four lines.
  • End on the tail: what this was filed under, and one way back.

Don't

  • No second gradient headline. The page's foil moment is the footer title.
  • No lime inside the text. Lime is light, and an article already spends its one moment in the figure or the footer.
  • Don't drop classes onto the elements inside .cf-prose — if it needs a class, it does not belong in an article body.
  • No index for a piece under four sections. A three-item index is furniture, not navigation.

The full page

patterns/blog-artikel.html is the whole thing: navigation, page header, index, body, tail, a “Weiterlesen” row built from the blog grid's own cell, and the footer.