Design language · Version 3.1 · 04.10.2026
Paper, ink and one burgundy.
This is how the BauLynq app looks and behaves — Designsprache v3, the drafting studio. Every value on this page is read from frontend/src/index.css and shown as a live specimen, in light and dark.
Typeface
Geist
13 px UI body · Geist Mono for identifiers
Themes
5
light · dark · material · contrast · dim
Radii
8 · 6 · 4
sheet · control · tag, in pixels
Controls
32 px
buttons, fields and table rows
01 · Principles
A drafting studio, not a brochure.
BauLynq is a tool for people who check numbers. The skin is warm paper, hairlines and one ink — quiet enough that the figures and the status carry the screen.
The studio
Decided with the owner on 2026-10-03. It borrows the drafting-table look of the KEHBIMVISION tools and keeps BauLynq's own colours. It covers the app at app.baulynq.com only — the marketing site and the promo films have their own identity (see Downloads).
Paper, not white
The canvas is warm paper (#f7f2ea); the sheets that carry work sit on it in off-white (#fffdf9). Dark is warm graphite, never blue-black.
Rules, not shadows
An edge is the ink mixed into transparent at one of six fixed strengths. Shadows are kept for things that float: menus, dialogs, the palette.
Quiet figures
13 px body, two ink tiers, tabular figures. Colour appears only when it means something — a status, an action, a focus.
Three colours, three jobs
The part most likely to be gotten wrong. Burgundy is the action and the place you are. Teal is where your attention is. Neutral ink is the value you picked. Ask of anything “selected”: is it a place (a page, a tab) or a value (a filter, a row)?
Burgundy — the action and the place
primary #8E2222, #E5726A on dark. The one primary action per view (the create or commit button) and the ⌘K search — and, since 2026-10-03, the selected page or tab: the active rail row, the tab and its underline, the selected view switch. Money, decision and blockage may use it as text or tint.
Teal — focus and “changed”
accent #0e6e7e, #5cc3cf on dark. The focus ring, text selection, links and “changed” markers; the edge of a toggled-on filter chip. Never an action, never a page.
Neutral — a chosen value
A selected row or card, an active filter, a checked box, a toggle: a neutral tint of the ink (8 %) and weight 500. Hover is 5 %. A value you picked, not a place you are.
SoftBadge tone={toneForStatus(…)}, never full-opacity badges. The ring is in Colour.Place or value?
One table settles every “selected” in the product. The tab recipe lives in one file, components/tabStyles.ts (tabClass, DAISY_TAB_ACTIVE, SEGMENT_*), and the design guard allowlists the files that carry it. The burgundy token flips to the AA-lifted value in the dark family, so nothing hard-codes a hex.
| What is selected | Recipe | Colour |
|---|---|---|
| Rail row, mobile nav item, module or section link, contents row | bg-primary/10 text-primary font-medium; the rail adds a 2 px bar | Burgundy |
Page tab — underline strip, masthead tabs, SectionTabs | text-primary font-medium and a 2 px inset underline; inactive ink-2, hover ink | Burgundy |
| Sheet tab — the LV's trade tabs | Lifted to the sheet, burgundy label and 2 px underline, a neutral count chip | Burgundy |
View switcher — SegmentedToggle, tone="nav" (default) | Raised segment on a recessed track, label text-primary font-medium | Burgundy |
Filter or form value — tone="choice", a filter chip | Raised segment with an ink label; a chip that is on takes the 8 % tint and border-accent/50 | Neutral, teal edge |
| Selected row or card, checkbox, toggle, radio | bg-base-content/[0.08] | Neutral |
| Focus ring, link, text selection, “changed” marker | The global 2 px ring, underline, 25 % tint | Teal |
One primary per view
A view has one filled burgundy button — the way to create the thing the view lists. An alternative route to the same result (an import next to “Neu”) is the flat outline.
Do
- One primary, leftmost in the toolbar.
- Neutral actions are the outline with a leading icon.
- Burgundy for the selected page or tab — a place.
Don't
- Two filled burgundies: neither one says “this is the main thing”.
- A burgundy filter chip, row or card to mean “selected”: that picks a value, not a place.
Rules that do not bend
Do
- Reach for a token by its job, never restate a percentage.
- Show a status as a tone:
SoftBadge tone=, with its word. - Lead a screen with a sentence about the work: “Fünf Dinge blockieren Geld.”
- Set identifiers in
.bl-id; money in Geist, tabular, no decimals in a list. - Design for all five themes and for a 390 px phone.
Don't
- Add a third grey. Quietness comes from size and weight.
- Hand-roll hex, shadows or
bg-white; nofont-mono. - Paint a selected filter, row or card burgundy — or use
border-primary/30orbl-soft-primaryas a selected state. - Use
rounded-fullbeyond avatars, dots and count badges. - Nest a card in a card: one sheet per page, sections divided by a hairline.
- Animate a figure someone is reading.
02 · Logo
One mark. No lockup.
The mark is a B of two interlocking links, 0.867 as wide as it is tall. It stands alone on the surface; the name, if it appears, sits at the far end.
The mark — this is the logo
One shape, filled with currentColor: ink on paper, light ink on graphite. Set the colour once on a parent and both themes are right (BrandMark in components/Brand.tsx). Size it by height; the viewBox is 277.28 × 320. Minimum 20 px high — below that the counters close up. Clear space is half the mark's width on every side.
#16161a#eeeae3size-7; write h-7 w-auto. The rounded-square badge exists only for favicons, avatars and placement over photography — files are in the brand pack (see Downloads).Rules
Do
- Use the bare mark. It is the logo.
- Set
coloronce and let one shape serve every theme. - Put the name, when needed, at the opposite end of the bar.
- One mark per surface.
Don't
- Build a lockup — not horizontal, not stacked, not “just this once”.
- Put the name beside, under or over the mark.
- Stretch, crop, recolour, outline or redraw it.
- Put a badge on a surface you control.
03 · Colour
Warm paper, two inks, one burgundy.
Colour is delivered as DaisyUI theme variables plus a handful of product tokens. All values below are the live ones; the specimens are painted from them.
Surfaces — paper, sheet, hairline, ink
Three surfaces and one ink. The canvas is not white — which is why a page sheet needs only a 1 px edge. base-300 is the opaque warm hairline for the few dividers that must not be translucent; almost every edge is a rule token (Surfaces and rules).
Light — warm paper
#f7f2eabase-200, --bl-surface
#fffdf9base-100: page, cards, dialogs
#e6e0d4base-300
#16161abase-content
Dark — warm graphite
#141311base-200, --bl-surface
#1c1b19base-100
#35322dbase-300
#eeeae3base-content
The five themes
light is the default. material shares its paper (and has softer chart hues). dark is warm graphite. contrast is true black with white ink; dim is a muted slate. The dark family lifts burgundy to #E5726A (dim: #EE8880) so it clears AA as text on its sheet, and puts near-black ink on every bright fill. The selected rail row and tab read the token, so they lift with it. Flip the specimen below through all five — the page theme is the sun / moon in the bar.
InrX · Rechnungen · In Prüfung
Drei Rechnungen warten auf Freigabe.
| Variable | light | dark | material | contrast | dim |
|---|---|---|---|---|---|
primary | #8E2222 | #E5726A | #8E2222 | #E5726A | #EE8880 |
accent | #0E6E7E | #5CC3CF | #0E6E7E | #5CC3CF | #5CC3CF |
base-100 sheet | #FFFDF9 | #1C1B19 | #FFFDF9 | #000000 | #232830 |
base-200 paper | #F7F2EA | #141311 | #F7F2EA | #111111 | #1C2128 |
base-300 hairline | #E6E0D4 | #35322D | #E6E0D4 | #2A2A2A | #2F3641 |
base-content ink | #16161A | #EEEAE3 | #16161A | #FFFFFF | #E6E9EE |
success | #006133 | #41BB88 | #006133 | #17C964 | #17C964 |
warning | #985E00 | #EFAF4C | #985E00 | #F5A524 | #F5A524 |
error | #B83B36 | #F77469 | #B83B36 | #F31260 | #F77469 |
info | #0972B2 | #51A9EF | #0972B2 | #14B4FF | #14B4FF |
Light's signal colours are pulled down in lightness so raw text-success, text-warning and text-error clear 4.5 : 1 on the sheet. The dark family uses brighter hues and near-black *-content on every fill (#0A0B0D; warning #150A00).
Two ink tiers — and no third
Tier 1 is the ink; tier 2 is the AA-safe quiet grey. Quietness comes from size and weight, never from more grey. In class names: text-base-content (or text-ink) and text-ink-2. Never an opacity grey below /70 on text; the legacy /40…/65 classes are remapped to tier 2 in the stylesheet so they stay legible.
Light
Abschlagsrechnung Mai 2026
Rohbau Nord · Freigabe am 12.06.2026 durch M. Kern
| Tier | Value | on sheet | on paper |
|---|---|---|---|
| 1 · ink | #16161a | 17.8 : 1 | 16.2 : 1 |
| 2 · ink-2 | #5b574f | 7.1 : 1 | 6.5 : 1 |
Dark
Abschlagsrechnung Mai 2026
Rohbau Nord · Freigabe am 12.06.2026 durch M. Kern
| Tier | Value | on sheet | on paper |
|---|---|---|---|
| 1 · ink | #eeeae3 | 14.4 : 1 | 15.5 : 1 |
| 2 · ink-2 | #a8a299 | 6.8 : 1 | 7.3 : 1 |
Action and focus colours against the sheet: burgundy 8.6 : 1 (light) and 5.7 : 1 (dark); teal 5.8 : 1 (light) and 8.3 : 1 (dark). A selected rail row is burgundy on its own 10 % tint over the paper: 6.6 : 1 (light), 5.4 : 1 (dark), 5.6 : 1 (dim, #EE8880).
The status ring
Six tones, one meaning each (lib/tone.ts). A tone is a 3 px tick at the left of a row (--bl-tone-*) and, where a column reports it, a pill on the matching tint (--bl-tint-*). Beton is a marker colour only — legible as a 3 px bar, not as 12 px text — so printed text uses --bl-tone-beton-text. In the dark family tints are low-alpha mixes of the tone itself.
| Tone | Means | Light · tick / tint | Dark · tick | Light | Dark |
|---|---|---|---|---|---|
gruen | freigegeben, bezahlt | #006133#E8FCEE | #41BB88 |
Freigegeben | Freigegeben |
bernstein | in Prüfung, Frist läuft | #985E00#FFF3DE | #EFAF4C |
In Prüfung | In Prüfung |
rot | überfällig, abgelehnt | #B83B36#FFEDE9 | #F77469 |
Überfällig | Überfällig |
beton | Entwurf, kein Status | #CBC5BA#EFEADF | #4A463Ftext #5B574F / #A8A299 |
Entwurf | Entwurf |
ox | Geld, Entscheidung, Blockade | #8E2222#F2DEDE | #E5726Adim #EE8880 |
Blockiert | Blockiert |
ink | a measured fact | #16161A#E3E7EE | #EEEAE3 |
Gemessen | Gemessen |
Dark tints: color-mix(in oklab, tone 16%, transparent) — beton mixes #9BA2AB at 12 %, ink mixes #F2F4F6 at 10 %. Dim lowers the rot tint to 12 % so error text on it stays 4.5 : 1. Neutral (beton, ink) means “not asked / not granted / a plain fact” — never merge it with a status.
Chart series
Six categorical slots, desaturated and distinct from the status colours so charts read as a separate data layer. Fixed order, never cycled — only neighbours touch. A status colour is never “series 4”; marks carry colour, label text wears ink. material has its own softer six (#57927D, #5F8EA9, #B1935A, #AF6B64, #8773A9, #6E8D7E); contrast and dim share one muted set (#8DB5A7, #92B2C5, #CAB68F, #C99A96, #ADA0C5, #9CB1A7).
Light
#A22626#0E6E7E#007A4B#4A6FA5#9B6000#9A5B8FDark
#D8574D#5CC3CF#19A572#7EA8E8#C57E00#C58AB904 · Type
One face, one density.
13 px is the UI body: a tool, not a brochure. Three weights, one scale, figures that line up.
Faces and weights
Geist (variable, Latin + Latin-extended, so äöüß and € are real glyphs) carries the voice and the figures. Geist Mono carries identifiers and nothing else (.bl-id; never font-mono). Both are self-hosted from /fonts — no third-party font request, which is also why the CSP can stay at font-src 'self'. Body text is set at letter-spacing: -0.005em with Geist's ss01 and cv11 alternates.
Geist
Aa Bb Cc Äö Üß € 0123456789
Die Schlussrechnung ist freigegeben und zahlbar.
Geist Mono — identifiers
AB-2026-033
OZ 02.140
R-2026-0147 · SHA-256 · /inrx/rechnungen
| Weight | Sample | Where |
|---|---|---|
| 400 | Regular | Body text, field values, table cells. |
| 500 | Medium | Headlines, titles, buttons, badges, figures, the active tab or row. |
| 600 | Semibold | Kickers and table heads (11 px, uppercase, 0.06 em). |
The scale
Tailwind's names are re-mapped, so text-sm is the UI body. Arbitrary sizes on the same scale are allowed: 11 / 12 / 13 / 14 / 15 / 18 / 22 / 24 px.
bl-kicker · tagsOffen zur Freigabe — labels, badges, table heads, keycapstext-xs · bl-metaZuletzt geändert von Markus Kern — notes, hints, dense (btn-xs) controlstext-sm · bodyDie Schlussrechnung ist freigegeben und zahlbar — buttons, fields, table cellstext-baseRechnung suchen oder Befehl eingeben — the palette input, running texttext-lg · bl-titleNachtrag anlegen — section and dialog titles, 500text-xlDrei Rechnungen warten — sub-headline, phone mastheadtext-2xlFünf Dinge blockieren Geld. — the page headlineFigures and identifiers
The face is chosen by what a string is, not by whether it contains digits. Money and quantities are values and belong to the sentence around them: Geist, tabular-nums, no decimals in a list — cents only where a document reconciles (an invoice total, the Mengen grid). Identifiers are read character by character: Geist Mono, .bl-id.
| Nr. | Bezeichnung | Netto, in a list | Total, on the invoice |
|---|---|---|---|
| AR-2026-011 | Abschlagsrechnung Juni | 317.854 € | 317.854,20 € |
| AR-2026-010 | Abschlagsrechnung Mai | 289.410 € | 289.409,85 € |
| NT-2026-007 | Nachtrag Fassade | 17.760 € | 17.760,00 € |
05 · Surfaces and rules
Rules, not shadows.
The app is a paper canvas, a rail on it, and one sheet beside the rail: topbar and page together. A 1 px edge, an 8 px radius, a barely-there lift. Sections divide with a hairline, not with more cards.
The shell — canvas, rail, one sheet
The root is the paper canvas (--bl-surface = base-200). The rail sits on it: no fill, no right border. Beside the rail is one sheet (.bl-sheet: sheet colour, 1 px --bl-rule, 8 px radius, the sheet shadow, 6 px in from the canvas edge) that holds both the topbar, on a bottom hairline, and the scrolling page. The page frame inside it — PageSurface, DataTable — is flat: it draws no card of its own, and its sections divide with border-t. Below lg (1024 px) there is no rail and no margin: the sheet is the whole screen and a four-tab bottom nav takes over.
InrX · Abrechnung & Freigaben
Fünf Dinge blockieren Geld.
| Nr. | Bezeichnung | Netto | Status |
|---|---|---|---|
| AR-2026-011 | Abschlagsrechnung Juni | 317.854 € | In Prüfung |
| AR-2026-010 | Abschlagsrechnung Mai | 289.410 € | Freigegeben |
| NT-2026-007 | Nachtrag Fassade | 17.760 € | Überfällig |
- Rail 208 px · 56 px collapsed
- Sheet 6 px from the canvas
- Topbar 48 px · 56 px below lg
- Page gutter
--page-gutter14 px · 0 below lg
Radii — 8, 6, 4
Pick by role, not by taste. rounded-xl and rounded-2xl are re-mapped to 8 px so a stray one cannot out-round the shell. rounded-full is for avatars, dots and count badges only; rounded-3xl (24 px) is the mobile bottom sheet's top edge only.
rounded-lg · page sheet, card, dialog, popover, menurounded-md · buttons, fields, selects, tabs' chipsrounded / rounded-sm · badges, keycaps, checkboxesThe rule tokens
Every edge is the ink (--color-base-content) mixed into transparent at a fixed strength, so one definition serves every theme. Reach for a token by its job — never restate the percentage at a call site. The line in each row is the token itself, drawn at 1 px.
--bl-rule-faint7 %Table rows, toolbar rule, the gaps in a figure strip.--bl-rule10 %Sheet edges, dividers, dialog header and footer hairlines.--bl-rule-card12 %A clickable sheet (bl-card) at rest.--bl-rule-button18 %The outline button's edge.--bl-rule-hover30 %A card or outline button under the pointer.--bl-rule-control50 % · 38 % darkInputs, selects, checkboxes — 3 : 1 against sheet and paper.bg-base-content/[0.05], outline buttons take base-200 and the hover rule. Selected or active is /[0.08].Shadows — only for what floats
shadow-sm is the sheet lift: a hairline plus a barely-there drop. shadow-md … 2xl all resolve to the floating-menu shadow, reserved for menus, popovers and dialogs. The dark themes swap both for a 1 px edge, because shadow on near-black is invisible.
Light
Dark
The backdrop behind a dialog, drawer or the palette is --bl-backdrop: 25 % ink with a 2 px blur on paper, and 55 % black in the dark themes (light ink would lift the page into a grey haze). Never bg-black/40.
Primitives — name the surface, never re-spell it
bl-panel — the page sheet
bl-popover — menus, dialogs, the palette
bl-card — clickable; its rule darkens
bl-strip — hairline gaps, for figures
bl-grid-paper — the 16 px drafting grid
bl-icon-tile · bl-keycap
Also: bl-title (15 / 500), bl-meta (12, ink-2), bl-kicker (11 uppercase eyebrow), bl-id (Geist Mono), and bl-tick — the 3 px inset tone marker at a row's left edge, an inset shadow so it never shifts a column.
06 · Density
Thirty-two pixels, no more.
A Bauleiter reads forty rows at a time. Controls and rows share one height so a toolbar, a form row and a table line up.
Controls and rows
btn-sm is the default button size: 32 px, 13 px type. A bare .input / .select — no size modifier — is 32 px too (only DaisyUI's --size moves, so an explicit h-10 on a touch-first form still wins), and table rows are 32 px. btn-xs (24 px, 12 px type) is for dense rows only; badges are 22 px (or 20 px compact).
Icons and gaps
Icons are lucide-react, imported directly: size-4 (16 px) at strokeWidth={1.75}. The rail's icons are 18 px at stroke 2; the empty-state tile's glyph is size-5. Gaps are 12–16 px; a dialog's header is px-5 py-3, its body px-5 py-4.
07 · Motion and layers
Motion explains a change.
One scale for the whole app, named by the job it does. If an animation explains nothing, it is decoration and does not ship.
The four durations — try them
Press play: each dot travels the track on its own token. The reduced-motion guard lives at the token layer — one media query collapses all four to 1 ms — so anything written in these tokens is automatically safe. This page obeys it too.
--bl-dur-tap 90 msPressed or hover — must read as instant. A press: Halten--bl-dur-ui 160 msControl state, tint, tick, badge.--bl-dur-enter 220 msSomething arrives: overlay, popover, row.--bl-dur-move 300 msSomething travels: sheet, drawer, reorder.The three easings
Easings say what the thing is doing: arriving decelerates, leaving accelerates, travelling settles long.
--bl-ease-out · cubic-bezier(0.22, 1, 0.36, 1)Overlays, popovers, rows.--bl-ease-in · cubic-bezier(0.4, 0, 1, 1)The backdrop, mostly — exits rarely animate.--bl-ease-move · cubic-bezier(0.16, 1, 0.3, 1)Sheets, drawers, maximizing a dialog.Rules
Do
- Animate
transformandopacityonly — they never trigger layout. - Let entrances animate; React unmounts exits at once, and a faked delay feels slow.
- Write durations as tokens:
duration-[var(--bl-dur-ui)]. - Stagger first paint only, capped at six rows.
Don't
- Animate height, width or top.
- Count a figure up. Money and status appear; they do not tick.
- Write a bare
duration-300or0.22s— it opts out of the reduced-motion guard. - Re-stagger a list on refetch.
Layering — a fixed scale
Never invent z-[10000]-style values. Overlays sit above the chrome so their backdrop dims the rail and the topbar. z-10 / z-20 are local stacking inside a context (a sticky table head) and are not steps on this scale.
| Layer | z | Examples |
|---|---|---|
| Page content | auto | everything in <main> |
| Mobile module sheet | z-30 | bottom sheet; under the chrome so the tab bar stays tappable |
| Shell chrome | z-40 | topbar, rail, bottom nav |
| Chrome popovers | z-50 | topbar menus, notification panel, banners |
| Overlays | z-[60] | Modal, Drawer, ConfirmDialog, the ⌘K palette |
| Lightbox | z-[70] | full-screen image viewer |
| Boot splash | z-[9998] | AppPreloader only |
08 · Components
A small kit, built from tokens.
Live specimens, painted from the tokens above. They copy the visual recipe of the app's components, not their markup. Use the bar to see the whole gallery in any of the five themes.
Buttons
Flat, 32 px, weight 500, a leading icon. One filled primary per view. Neutral actions are the outline. Ghost is for icon-only buttons and a dialog's Cancel. Destructive is error-coloured text, and only the confirm step is filled.
Fields
32 px, 13 px, a 6 px radius and a --bl-rule-control edge (3 : 1). A bare .input or .select is 32 px by itself, and the text of a raw .input, .select or .textarea is 13 px — not the browser's 16, not smaller. Focus is one quiet teal ring — not the double ring DaisyUI ships. An invalid field turns its edge error-red and says why in 12 px. A caller that needs another border colour on a field uses the important suffix (border-accent/50!): the field voice is unlayered and outranks a plain utility.
Badges
Soft tints only, 11 px / 500, a 4 px radius, 22 px tall (20 px compact). Anything that is a status takes a tone from the ring: SoftBadge tone={toneForStatus(row.status)}. color= stays for categorical chips — a kind, a priority, a code.
Tabs and view switchers
A selected page or tab is burgundy (owner decision 2026-10-03): a text-primary font-medium label and a 2 px burgundy underline, inset so it never shifts layout. Inactive is ink-2, full ink on hover; 32 px, 13 px, icon + label; focus is the inset teal ring. The recipe is one file, components/tabStyles.ts. A page's view switcher lives under its headline (Masthead), not in the topbar.
SegmentedToggle, tone="nav", the default) the selected label is burgundy. As a filter or a form choice (tone="choice") it stays ink — it picks a value, not a place. The lifted sheet tabs (.sheet-tab, the LV's trade strip) wear the same burgundy label and underline on the sheet colour, with a neutral count chip that takes a 14 % burgundy tint when selected.Figure strip
Four cells on hairline gaps (bl-strip): an 11 px label, a 22 px / 500 figure, a 12 px note. No icon chips. A figure takes a colour only when it means “this is the bad one”.
Zur Freigabe
12
3 älter als 14 Tage
Zahlbar
317.854 €
AR-2026-011 · netto
Überfällig
17.760 €
1 Nachtrag, 9 Tage
Freigegeben 05/26
289.410 €
4 Rechnungen
Masthead
A kicker for scope and state, then a sentence about the work — “Fünf Dinge blockieren Geld.” beats “Dashboard”. 24 px / 500 with tight tracking (18 px on a phone), one line where it fits. It is the page's only <h1>. The screen's own non-creation actions sit right; its view switcher sits under it, and its selected view is burgundy. From xl the identity can dock into the topbar (next).
InrX · Abrechnung & Freigaben
Fünf Dinge blockieren Geld.
Zwei Aufmaßblätter warten auf Freigabe, ein Nachtrag ist überfällig.
Docked masthead
“If there is nothing on the topbar far left, move the whole page data up.” From xl (1280 px), when the topbar's left holds only the back arrow — no scope switcher, no crumb trail — the masthead's identity (kicker, record number and status, the 24 px headline) moves into that empty band, so the page starts right under the topbar instead of one band lower. The subtitle, the actions, the tabs and the figure strip stay in the page. The specimens are drawn at real size and scaled to fit the stage.
Fünf Dinge blockieren Geld.
Zwei Aufmaßblätter warten auf Freigabe, ein Nachtrag ist überfällig.
ExportierenZur Freigabe
12
3 älter als 14 Tage
Zahlbar
317.854 €
AR-2026-011 · netto
Überfällig
17.760 €
1 Nachtrag, 9 Tage
Freigegeben 05/26
289.410 €
4 Rechnungen
Fünf Dinge blockieren Geld.
Zwei Aufmaßblätter warten auf Freigabe, ein Nachtrag ist überfällig.
ExportierenZur Freigabe
12
3 älter als 14 Tage
Zahlbar
317.854 €
AR-2026-011 · netto
Überfällig
17.760 €
1 Nachtrag, 9 Tage
Freigegeben 05/26
289.410 €
4 Rechnungen
/inrx… and /aufgaben… page), so the masthead keeps its own band.Viewport
≥ 1280 px
xl; below it, and on a phone, nothing changes
Slot
≥ 288 px
else the masthead stays in the page band
Lines
≤ 2 · ≤ 2 · 1
headline · kicker · record-number row
Height
≤ 76 px
identity only; always saves space over the inline band
The docked stack is tight on purpose: a 13 px kicker line over a 26 px headline line is 40 px, so a one-line kicker over a one-line headline fits the 48 px row exactly and the bar does not grow. A wrapped line grows the bar by its extra lines only; the extra height over 48 px is published as --bl-dock-extra and is part of --topbar-height (calc(3rem + var(--bl-dock-extra))), so a fill page still ends at the sheet's bottom. The arrow, the search and the right cluster keep their 48 px row at the top — they never move between pages. No state exists in which the text is cramped or clipped: it either fits the rules or stays inline.
Journal table
Lists are journals. A paper head band with an 11 px uppercase head under a --bl-rule hairline, 13 px body, 32 px rows, --bl-rule-faint between rows, hover 5 %. A row states its tone as a 3 px tick at the left edge, and the status column repeats it in words. The creation button sits far left; filters and search sit far right.
Empty state
A bl-grid-paper frame with an icon tile, a bl-title that says what is missing, and a hint that says what would create the first record. When the same primary already sits above, the action here is the outline.
Noch keine Rechnungen
Eine Rechnung entsteht aus freigegebenen Aufmaßblättern. Legen Sie die erste an.
Dialogs
A bl-popover window that stays draggable (grab the header — the grip icon says so), resizable (corner grip), and expandable (the maximize button, or a double-click on the header). Below 640 px it becomes a bottom sheet. Header px-5 py-3 on a hairline, title in bl-title, footer hairline with the primary action last. Its icon chip is neutral unless a severity colour is passed — burgundy is the action, not the dialog's decoration. ConfirmDialog and GateDialog inherit it.
Nachtrag anlegen
Nachtrag anlegen
Nachtrag anlegen
- Phone sheet: 24 px top radius, a drag handle, no grip or maximize, buttons stacked full-width with the primary on top.
- Width is the content's, never more:
smfor confirmations and a few fields,mdfor one column,lgfor two or three columns,xland up for tables and card grids. - Backdrop:
--bl-backdropwith a 2 px blur. - Keep the exact size-tier class strings — a unit test asserts them.
Rail
The rail sits on the canvas and is module-scoped: it lists the current module's screens in the order the work happens — Projekt-Setup, Ausführung, Abrechnung. It is collapsible: 208 px with icon and label, 56 px with icons only, remembered per browser (bl-rail-collapsed); the sheet's margin follows through --bl-rail. The mark, the row icons and the collapsed buttons share one axis, so nothing jumps when it folds.
- Brand block: an
h-12row level with the topbar; the mark ath-6, the wordmark 13 / 600, the company's name beneath it at 11 px in ink-2 once it is known (only Manager and up can read it, so for others it is the wordmark alone). It links to the hub. - Module header: icon and name in
bl-title, a plain label. The rail carries no back link — the topbar's arrow is the only way up. - Selected row: burgundy —
bg-primary/10 text-primary font-mediumand a 2 px bar at the left edge. Hover is a neutral 5 %. - Counts sit right and only when really read (money in burgundy); collapsed they become a tiny badge on the icon, and a group heading becomes a 20 px hairline.
- Collapsed rows are 36 px icon buttons; the label is the tooltip.
- Icons 18 px at stroke 2; the toggle's
PanelLeftCloseis 16 px at 1.75.
Topbar and back arrow
The topbar lives inside the sheet: 48 px (56 below lg) on a bottom hairline. From md it is a three-column grid — 1fr · search · 1fr, the search at most 560 px and 40 % of the row — so the ⌘K field is centred in the sheet's width, measured 0.0 px off at 1024, 1280, 1440 and 1920 px, rail open or collapsed. Left: the back arrow, then the scope switcher and crumbs. Right: the page's own actions, the notification bell and “Mehr”. There is no avatar: the account menu moved to the rail.
| Screen | The arrow goes to | Label |
|---|---|---|
A page that registers its own backPath or onBack | where it says — this wins over everything below | its own |
| A sub-screen | its parent screen | Zurück |
A module's top-level screen — a rail row, a module dashboard, /verwaltung | the hub, / | Alle Module |
| A standalone page — project record, Abläufe, workspace, Docs, Changelog, Benachrichtigungen | the hub, / | Alle Module |
| The hub, the 404, login | no arrow | — |
btn btn-ghost btn-sm btn-square: text-ink-2, hover:text-accent, a 16 px arrow at stroke 1.75, the tooltip is its label, a 40 px touch target below lg, the teal focus ring. It navigates, so the unsaved-changes gate still asks Save, Discard or Stay. Resolved in one place, backTargetFor() in app/modules.ts.Account menu — the rail footer
Like tomweb's account block, the account menu is the footer row of the rail: a 28 px round avatar, name, role and a chevron — collapsed, the avatar alone with a tooltip. It opens a bl-popover (240 px) upward, portalled to <body> and positioned from the trigger so a 56 px rail never clips it. Items: change password, the Claude connector, and sign out in the error colour. Roving focus, Esc, arrows, Home and End; Tab closes; focus returns to the trigger. The topbar's right cluster has no avatar and the footer has no settings gear — one affordance per target. Below lg it is the last row of the bottom nav's Module sheet.
Awesomebar — ⌘K
The search owns the topbar centre (the middle column of the grid above, centred on the sheet) and is the one chrome control the owner reserved burgundy for, next to the selected page or tab. Closed, it is a field (max 560 px, 32 px tall, 6 px radius, --bl-rule-control edge) with a burgundy search icon and a ⌘K keycap; its edge goes burgundy on hover and focus. It finds records, pages and commands.
Open, it is a bl-popover palette: a burgundy search icon and caret, group headings in bl-kicker, record ids in .bl-id, shortcuts as keycaps, and the active result as a neutral tint with a 2 px burgundy bar at its left.
Datensätze
Seiten
Befehle
Drawer
The dialog's chrome on a side panel, for read-mostly detail. Its inner edge carries a drag strip for width; it is also expandable — a maximize toggle (or a double-click on the header) turns it into a floating sheet. It does not drag to move. Below 640 px it is a bottom sheet like Modal.
AR-2026-011
- Status
- In Prüfung
- Netto
- 317.854 €
- Zeitraum
- 01.06.–30.06.2026
- Projekt
- Rohbau Nord
09 · Voice
Write like a Bauleiter.
German, direct, technically literate. Short sentences, exact nouns, no adjective doing a number's job. v3 changes how the app looks, not what it says.
Tone
Do
- „Von der Ausschreibung bis zur Übergabe geht nichts verloren."
- Name the artefact: Leistungsverzeichnis, Aufmaß, Nachtrag, Abschlagsrechnung.
- Cite the rule where it earns trust: VOB/B § 16.
- Headlines of one or two clauses; sub-copy of one sentence.
- Use „Sie". Address a Bauleiter, not a persona.
Don't
- „Revolutionäre KI-gestützte Cloud-Lösung."
- Anglicisms where a German word exists — Abrechnung, not Billing.
- Exclamation marks. Ever.
- Claims without a figure behind them.
- Emoji, on any surface.
Naming
BauLynq — one word, capital B and L, everywhere. Module names end in a capital X (InrX) with no space or hyphen. The app is German-first: every string goes through i18n in both languages, and records keep their German names.
10 · Downloads
The brand pack is not the app spec.
The app's tokens are v3. The pack below is the marketing identity: logo files, Office templates and the brand-v2 manual.
App tokens — Designsprache v3
Brand pack — logo, marketing colours, Office templates
Nothing here has been regenerated for v3.
Using the tokens
<link rel="stylesheet" href="/designlanguage/assets/tokens/baulynq-tokens.css">
<div data-theme="dark">…</div> /* any element can carry a theme */
.sheet {
background: var(--color-base-100);
border: 1px solid var(--bl-rule);
border-radius: var(--radius-box);
box-shadow: var(--bl-shadow-sheet);
}
.figure {
font-size: 22px; font-weight: 500;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
}
In the app, use the Tailwind classes and primitives (bg-base-100, text-ink-2, bl-panel) rather than the raw variables; frontend/src/index.css is where the values change.