kreiseck_design
Design tokens, a Material theme and the brand assets used across Kreiseck's
Flutter apps. The values — colour ramps, roles, radii, spacing, type scale —
come from one token source and are generated for this package and for its
npm twin at the same time; a golden fixture shared between the two is what
keeps them in sync, not a hand-maintained comparison. An app reads a role
(kdColor(mode, 'brand')), never a ramp step, so a palette change does not
mean hunting down colour literals.
Installation
dependencies:
kreiseck_design: ^0.2.0
Usage
Build a theme from a mode and hand it to MaterialApp:
import 'package:flutter/material.dart';
import 'package:kreiseck_design/kreiseck_design.dart';
MaterialApp(
theme: kdTheme(KdMode.light),
darkTheme: kdTheme(KdMode.dark),
home: const MyHomePage(),
);
Read a colour by role rather than by ramp step:
Container(
color: kdColor(KdMode.light, 'brand'),
child: const Icon(Icons.check, color: Colors.white),
);
Amounts, numbers and anything else that stands in a column and gets compared should use the monospace style, not the body text:
Text('€ 42,00', style: kdMonoStyle(KdMode.light));
KdMode has four values — light, dark, warm and contrast — and
kdTheme, kdColor and kdTextTheme all take one to resolve against.
Brand colour at runtime
brandRamp turns an arbitrary seed colour into the same eleven-step ramp
the shipped palette is built from. The hue stays the customer's; the
lightness is the ladder's — so a bright yellow becomes a dark gold on
which white text is readable. Step 700 carries that guarantee: white text
on it reads at 4.5:1 or better for any seed.
final ramp = brandRamp(const Color(0xFFFFD400));
final button = ramp[700]!; // #705C00 — white text on it reads at 6.5:1
Icons
Every icon shares one hand: a 24-unit grid, stroke 1.75, round caps and
joins. KdIcon paints it as a CustomPainter, so it slots in anywhere a
Material Icon would.
KdIcon(KdIcons.receipt);
With no size or color, it falls back to the ambient IconTheme — set
one on an IconTheme.merge or a widget's iconTheme and every KdIcon
underneath it follows. Either can also be given explicitly, and a
semanticLabel makes the icon speak to screen readers instead of staying
decorative:
KdIcon(KdIcons.receipt, size: 20, color: kdColor(KdMode.light, 'brand'));
KdIcon(KdIcons.check, semanticLabel: 'Bestätigt');
Eleven icons also come as a filled variant, named with a Filled suffix —
KdIcons.receiptFilled, KdIcons.checkFilled — for a selected or emphasised
state next to the plain outline for the rest. Look any icon up by its source
name (the id used in the icon repository) instead of the generated Dart
name with KdIcons.byName:
KdIcons.byName['cash-drawer'];
The set covers seven groups; one example each:
- navigation —
KdIcons.arrowLeft - action —
KdIcons.edit - device —
KdIcons.monitor - cash —
KdIcons.receipt - status —
KdIcons.checkCircle - document —
KdIcons.file - people —
KdIcons.user
The logo
The Kasseneck logo is drawn from geometry, no asset and no font: the mark as the three brand paths, the wordmark as Archivo 600 outlines. What you see is pixel for pixel the brand's SVG.
KdSignet(size: 40) // the mark: frame in the icon colour, corner in colorScheme.primary
KdLogo(height: 28) // mark + wordmark, 332/48 as wide as tall
KdWordmark(height: 28) // the wordmark alone
Colours: frame/ink and square/accent (on a dark ground pass a light
ink); highlight is the light petrol a tinted cell blends toward.
The mark carries the brand, not the business. On a surface themed in a
customer's own colour (colorScheme.primary is theirs there) pass the brand
petrol explicitly — square: KdRoles.light['brand'] (or the mode's roles) — and never recolour the
corner with the customer's colour: a mark in another colour is a different
mark. The customer's colour belongs to the controls around it.
Animations
The mark is an 8×8 grid: 32 cells of the frame and the corner, plus a pixel
layer over all 64 fields that is invisible at rest — so the mark can show
pixel art and become the logo. Every animation is data
(brand/animations.json), identical in this package and @kreiseck/design.
KdLogoMotion(animation: KdLogoAnimations.heart, height: 40)
KdLogoMotion(animation: KdLogoAnimations.breathe, wordmark: false, height: 64) // the mark alone, loops
KdLogoMotion(animation: KdLogoAnimations.byName['euro']!, onDone: () => go())
| name | label | scenario | length |
|---|---|---|---|
rain |
Regen | splash | 1600 ms |
bloom |
Aufblühen | splash | 1400 ms |
scatter |
Zufall | splash | 1400 ms |
heart |
Herz | splash | 2150 ms |
heartbeat |
Herzschlag | splash | 2850 ms |
euro |
Euro | splash | 2100 ms |
check |
Haken | splash | 2100 ms |
star |
Stern | splash | 2100 ms |
lock |
Schloss | splash | 2100 ms |
receipt |
Bon | splash | 2100 ms |
mail |
Brief | exit | 2100 ms · hält bei 1250 |
confirm |
Bestätigt | state · success | 1600 ms · hält bei 1000 |
error |
Fehler | state · danger | 1450 ms · hält bei 880 |
breathe |
Atmen | waiting | 2400 ms · loops |
loop overrides the animation's own flag, autoplay: false shows the last
frame, and KdLogoMotionState.play() restarts (each run draws its own order
for tracks marked random).
An animation in the state column names a colour role rather than a colour,
and is drawn in kdColor(mode, 'success' | 'danger') for the ambient
brightness; a highlight: you pass in wins.
Some animations also carry cues — moments that tap and sound. haptics: true
turns on the touch half, which Flutter carries itself. The sound half is handed
to you, so this package needs no audio dependency: a tone is a spec, and
kdRenderWav turns it into bytes for whatever player the app already has.
hold: true stops at the animation's hold point and stays there: the check
keeps standing, the cross keeps showing, the envelope waits instead of flying
off. It is the moment the sign is finished and nothing is on its way any more
(KdLogoAnimations.confirm.hold, in ms). A held run plays only the cues it
reaches, and never loops.
KdLogoMotion(
animation: KdLogoAnimations.confirm,
haptics: true,
onCue: (cue) {
final tone = KdSounds.byName[cue.sound];
if (tone != null) player.play(BytesSource(kdRenderWav(tone)));
},
)
For your own motion, sample the data and hand the styles to the widgets:
final s = kdSampleLogo(KdLogoAnimations.rain, t); // t 0..1
KdLogo(height: 28, cells: s.cells, glyphs: s.glyphs, pixels: s.pixels)
A KdLogoStyle per element carries opacity, scale, dx/dy (in cells),
tint (toward highlight) and accent (toward the corner's petrol). At rest
the widgets paint the brand paths, so a static logo never shows seams
between cells.
Fonts and licence
Archivo and DM Mono ship with the package (fonts/) under the SIL Open
Font License; the corresponding OFL-Archivo.txt and OFL-DMMono.txt
travel with them. The code itself is MIT — see LICENSE.
Source
Libraries
- kreiseck_design
- Design tokens and theme for every Kreiseck surface.