Komponenten
Formensprache
Achteck mit gekappten Ecken — nie ein Radius, nie eine Spitze.
Farben — dieselbe Form, andere Bedeutung
Die Form ist farbunabhängig. Alle Werte in oklch, unverändert aus globals.css.
--goldPrimär, Ring, Akzent--gold-dimSekundäre Stimme--destructiveZerstörende Aktionen--warningWartet auf Freigabe--cardKarten, Panels--bevelKante der Schräge — deckendKappung — zwei Reihen
Ein Wert kann nicht funktionieren: Kontrollen bleiben subtil (auf einem 32-px-Knopf war mehr zu krass), Flächen tragen die Schräge deutlich, sonst wirkt sie zaghaft.
Formensprache
Formensprache
Die Tessera-Form: Kappung statt Rundung, Kante als evenodd-Ring, gemessene Bevel-Stufen. Grundlage aller weiteren Items.
Die Formensprache reist als Datei, byte-identisch — nicht als übersetzte Tokens. Damit gibt es eine Quelle und nichts, was auseinanderlaufen kann. Wer sie zieht, bekommt auch die Reparaturregeln, und die müssen unlayered bleiben: in Tailwind v4 schlägt der @layer-Rang die Spezifität. Ebenso zu wissen: handgeschriebene @layer utilities-Klassen bekommen keine responsiven Varianten — sm:tsr-cut wird nie erzeugt.
npx shadcn@latest add @tessera/formPrimitive
Button
Kappung an der Groessenvariante, nicht an der Aufrufstelle. Umriss-Variante traegt die Kante als Ring.
Die Kappung hängt an der Größenvariante, nicht an der Aufrufstelle. Vorher stand sie in der className jedes Aufrufs — 19 Stellen, von denen einzelne immer vergessen wurden. Die Umriss-Variante trägt ihre Kante als Ring, nicht als border: auf einer Kappung überlebt ein Rahmen nur die geraden Kanten, die Schrägen bleiben leer.
npx shadcn@latest add @tessera/buttonZieht mit: @tessera/form, utils
Card
Kappung 12px, Kante als Ring. Keine Rundung, kein ring-Utility — beide sind auf einer Kappung unsichtbar.
Kein ring-1. Ein ring liegt außerhalb der Box und wird von der Kappung vollständig entfernt — nicht halb, gar nicht. Dasselbe gilt für outline und box-shadow. Übrig bleiben zwei Wege: der evenodd-Ring im Pseudoelement oder farbige Polsterung auf einer gleich geformten Hülle.
npx shadcn@latest add @tessera/cardZieht mit: @tessera/form, utils
Dialog
Kappung 15px, mit Kopf, Fuss und Beschreibung. Traegt eine eigene Regel fuer den responsiven Fall, in dem der Dialog erst ab sm eine Kappung bekommt.
npx shadcn@latest add @tessera/dialogZieht mit: @tessera/form, utils
Input
Traegt die Form, aber bewusst keine Huelle: die Kante kommt von FieldShell, weil sonst die className der Aufrufstellen mehrdeutig wird.
Trägt die Form, aber bewusst keine Hülle. Die Kante käme sonst aus der className von zwanzig Aufrufstellen und wäre dort mehrdeutig —tailwind-merge lässt etwa p-3.5 gegen py-(--card-spacing) gewinnen. Für Felder mit Kante: FieldShell.
npx shadcn@latest add @tessera/inputZieht mit: @tessera/form, utils
Dropdown Menu
Kappung und Ring statt Schatten.
Kappung und Ring statt Schatten. Im geöffneten Zustand am besten in der Anwendung selbst zu sehen.
npx shadcn@latest add @tessera/dropdown-menuZieht mit: @tessera/form, utils
Tooltip
Kappung und Ring statt Schatten.
Kappung und Ring statt Schatten.
npx shadcn@latest add @tessera/tooltipZieht mit: @tessera/form, utils
Sheet
Seitliches Panel auf Radix-Dialog. Kappung ueber die Rollen-Selektoren der Formensprache.
Kappung 15px über die Rollen-Selektoren. Ein geöffnetes Sheet überdeckte den Katalog — am besten im app-shell auf schmalen Schirmen zu sehen.
npx shadcn@latest add @tessera/sheetZieht mit: @tessera/form, utils
Skeleton
Ladeplatzhalter. Traegt keine Kante — eine Flaeche, die noch niemand ist.
npx shadcn@latest add @tessera/skeletonZieht mit: @tessera/form, utils
Separator
Trennlinie in Haarstaerke.
Über der Linie
Unter der Linie
npx shadcn@latest add @tessera/separatorZieht mit: @tessera/form, utils
Tabs
Base-UI-Tabs. Die Kappung der Ausloeser kommt zentral ueber [role=tab] aus der Formensprache.
[role=tab].npx shadcn@latest add @tessera/tabsZieht mit: @tessera/form, utils
Breadcrumb
Brotkrumen-Pfad; lange Pfade klappen die Mitte in ein Dropdown (siehe page-shell).
npx shadcn@latest add @tessera/breadcrumbZieht mit: @tessera/form, utils
ScrollArea
Scroll-Flaeche mit eigener, schmaler Leiste.
Zeile 1 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 2 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 3 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 4 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 5 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 6 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 7 — die Leiste gehört der Fläche, nicht dem Fenster.
Zeile 8 — die Leiste gehört der Fläche, nicht dem Fenster.
npx shadcn@latest add @tessera/scroll-areaZieht mit: @tessera/form, utils
Collapsible
Auf- und zuklappender Ast, traegerlos — die Form kommt vom Inhalt.
npx shadcn@latest add @tessera/collapsibleZieht mit: @tessera/form, utils
Table
Datentabelle mit Kopf, Fuss und Zeilenmarkierung. Grundlage jedes Gitters.
| Paket | Fassung | Größe |
|---|---|---|
| @dev0zzz.tessera.at/sdk | 0.4.1 | 184 kB |
| @dev0zzz.tessera.at/cli | 0.2.0 | 92 kB |
| @dev0zzz.tessera.at/ui | 0.1.3 | 311 kB |
| Gesamt | 587 kB | |
npx shadcn@latest add @tessera/tableZieht mit: @tessera/form, utils
Pagination
Seitenwechsel als nav mit Liste und aria-current. Bringt paginationRange mit — welche Zahlen sichtbar sind, entscheidet nicht die Aufrufstelle.
Seite 7 von 42
npx shadcn@latest add @tessera/paginationZieht mit: @tessera/form, @tessera/button, utils
ColumnResizer
Ziehgriff fuer Spaltenbreiten. Button mit role=separator statt div — mit Pfeiltasten bedienbar, Breite als aria-valuenow.
| Fassung | Kanal |
|---|---|
| v0.5.2 — Formensprache | Radicle |
| v0.5.1 — Dialoge | Radicle |
180 px — Pfeiltasten ±8, mit Umschalt ±80, Doppelklick setzt zurück
npx shadcn@latest add @tessera/column-resizerZieht mit: @tessera/form, utils
DataGrid
Tabelle mit Sortierung, Auswahl und ziehbaren Spalten. Haelt keinen Zustand — sortiert wird bei jeder ernsthaften Menge auf dem Server.
| v0.0.0 | Radicle | zurückgezogen | 2026-01-01 | 120 | |
| v0.0.1 | GitHub | signiert | 2026-02-02 | 157 | |
| v0.0.2 | npmjs | signiert | 2026-03-03 | 194 | |
| v0.0.3 | Radicle | Entwurf | 2026-04-04 | 231 | |
| v0.1.0 | GitHub | signiert | 2026-05-05 | 268 | |
| v0.1.1 | npmjs | signiert | 2026-06-06 | 305 | |
| v0.1.2 | Radicle | Entwurf | 2026-07-07 | 342 | |
| v0.1.3 | GitHub | zurückgezogen | 2026-08-08 | 379 |
npx shadcn@latest add @tessera/data-gridZieht mit: @tessera/form, @tessera/table, @tessera/checkbox, @tessera/column-resizer, @tessera/skeleton, utils
Checkbox
Kaestchen — benannte Ausnahme der Formensprache: es behaelt seine eigene Form.
npx shadcn@latest add @tessera/checkboxZieht mit: @tessera/form, utils
Label
Beschriftung eines Feldes, verknuepft ueber htmlFor.
npx shadcn@latest add @tessera/labelZieht mit: @tessera/form, utils
Textarea
Mehrzeiliges Feld. Ohne Ziehgriff — der sitzt in der Ecke, die die Kappung wegschneidet; die Hoehe kommt aus rows, das Mitwachsen schaltet die Aufrufstelle zu.
npx shadcn@latest add @tessera/textareaZieht mit: @tessera/form, utils
InputGroup
Feld mit Beiwerk — Lupe davor, Knopf dahinter, Zaehler darunter. Die zweite Huelle neben FieldShell, weiterhin genau ein Feld pro Huelle.
npx shadcn@latest add @tessera/input-groupZieht mit: @tessera/form, @tessera/input, @tessera/textarea, utils
Popover
Freie Flaeche an einem Ausloeser. Kein Menue — die Pfeiltasten bleiben unangetastet, damit ein Formular darin bedienbar ist.
npx shadcn@latest add @tessera/popoverZieht mit: @tessera/form, utils
Calendar
Monatsraster auf react-day-picker. Der Balken einer Spanne bleibt ein Rechteck — gekappt wird der Tagesknopf darauf.
| Mo | Di | Mi | Do | Fr | Sa | So |
|---|---|---|---|---|---|---|
| Mo | Di | Mi | Do | Fr | Sa | So |
|---|---|---|---|---|---|---|
npx shadcn@latest add @tessera/calendarZieht mit: @tessera/form, @tessera/button, utils
DatePicker
Datum und Zeitraum aus einem Kalender im Popover. Der Ausloeser ist ein Feld, kein Knopf; getippt wird nicht.
npx shadcn@latest add @tessera/date-pickerZieht mit: @tessera/form, @tessera/calendar, @tessera/popover, utils
Select
Auswahl aus einer festen Liste. Der Ausloeser ist ein Feld, keine Schaltflaeche — Hoehe und Kappung folgen dem Input.
npx shadcn@latest add @tessera/selectZieht mit: @tessera/form, utils
Combobox
Auswahl mit Tippen, einfach und mehrfach. Die Mehrfachauswahl steckt mit drin — Chips als Huelle, ein Chip je gewaehltem Wert.
npx shadcn@latest add @tessera/comboboxZieht mit: @tessera/form, utils
ToggleGroup
Segmentierte Auswahl. Die Gruppe traegt die Kappung des Kastens, jeder Knopf darin seine eigene.
npx shadcn@latest add @tessera/toggle-groupZieht mit: @tessera/form, utils
Sidebar
Die shadcn-Sidebar-Primitive: Provider, Flaechen, Menue, Rail. Grundlage des app-shell.
npx shadcn@latest add @tessera/sidebarZieht mit: @tessera/button, @tessera/input, @tessera/tooltip, @tessera/sheet, @tessera/skeleton, @tessera/separator, @tessera/form, utils
Atome
Badge
Kappung 6px.
npx shadcn@latest add @tessera/badgeZieht mit: @tessera/form, utils
CodeBlock
Rendert code oder pre und ersetzt die sieben identischen Klassenketten, die vorher von Hand wiederholt wurden.
npx shadcn@latest add @tessera/buttonnpx shadcn@latest add @tessera/code-blockZieht mit: @tessera/form, utils
FieldShell
Kappung und Ring fuer ein einzelnes Feld. Traegt die Kante, die das Input bewusst nicht selbst traegt.
npx shadcn@latest add @tessera/field-shellZieht mit: @tessera/form, utils
Toggle
Zweiwertiger Umschalter, gekappt.
npx shadcn@latest add @tessera/toggleZieht mit: @tessera/form, utils
SearchInput
Suchfeld mit Lupe, gekappt und mit Kante.
npx shadcn@latest add @tessera/search-inputZieht mit: @tessera/form, @tessera/input, utils
Logo
Der Achteck-Pfad. Ursprung der 25-Prozent-Kappung: 14 von 56 Einheiten in einer 64er-Box.
Ursprung der Kappung. Der Pfad M18 4 H46 L60 18 … in einer 64er-Box: die Kante läuft von 4 bis 60, also 56 Einheiten, davon 14 gekappt — 25 %. Nicht die 29,29 % des regelmäßigen Achtecks; die sind geometrisch richtig und für diese Marke trotzdem falsch.
npx shadcn@latest add @tessera/logoTesseraStart
Der Marken-Knopf.
Gefüllt, nie umrandet-mit-hohlem-Kern. Das Gold gehört der Hauptaktion, und die ist Create @handle — diese Knöpfe sieht nur, wer abgemeldet ist, und der ist meist neu hier. Drei Entwürfe liegen dahinter: Goldrand mit hohlem Kern (überkonstruiert), Gold für beide (dann sagt die Farbe nicht mehr, was primär ist), Betonung vertauscht. Jetzt leitet die Komponente die Optik aus intent ab, damit eine App sie nicht falsch wählen kann. Beide Füllungen sind deckend, also vom Untergrund unabhängig.
npx shadcn@latest add @tessera/tessera-startZieht mit: @tessera/form, @tessera/logo, utils
OrgCrest
Wappen einer Organisation, im Achteck.
npx shadcn@latest add @tessera/org-crestZieht mit: @tessera/form, utils
ActorAvatar
Form nach Herkunft: Tessera-Konten im Achteck mit Goldrand, alle anderen rund. Der Rand ist Polsterung auf gleich geformter Huelle, weil ein ring auf einer Kappung verschwindet.
Die Form sagt die Herkunft: Tessera-Konten im Achteck mit Goldrand, alle anderen rund. isTesseraActor prüft mit endsWith auf .tessera.at — nie mit includes, sonst könnte sich tessera.at.evil.example als heimisch ausgeben. Bekannte Grenze: Tessera-Konten auf eigener Domain lesen als fremd und bekommen den Kreis.
npx shadcn@latest add @tessera/actor-avatarZieht mit: @tessera/form, @tessera/actor-origin, utils
ActorIdentity
Wie ein Konto in Tessera erscheint: Avatar, Anzeigename, darunter das Handle mit @. Eine Figur fuer jede Stelle, an der ein Konto auftaucht.
npx shadcn@latest add @tessera/actor-identityZieht mit: @tessera/actor-avatar, @tessera/form, utils
ContentSurface
Die Flaeche, auf der der Anwendungsinhalt sitzt: ein eigener Layer unter der Tab-Leiste, mit Tessera-Kante und optional mit vertikalen Reitern am Rand.
Beschreibung
Der Inhalt steht auf einer eigenen Platte mit Tessera-Kante. Die Reiter am Rand gliedern innerhalb des Tabs weiter und wechseln mit der Fläche, nicht mit der App.
npx shadcn@latest add @tessera/content-surfaceZieht mit: @tessera/form, utils
SearchSyntaxHint
Das Fragezeichen am Suchfeld: zeigt feld:wert, Phrase, Ausschluss und Groessenvergleich mit den echten Feldnamen der Suche. Eine Syntax, die niemand sieht, benutzt niemand.
npx shadcn@latest add @tessera/search-syntax-hintZieht mit: @tessera/popover, @tessera/button, @tessera/form
Gefüge
AppShell
Das Grundgeruest einer Tessera-App: Sidebar mit Navigationsbaum, Inhalt als Inset. Kopf, Fuss und Suche sind Slots — Business gehoert in die Page.
npx shadcn@latest add @tessera/app-shellZieht mit: @tessera/sidebar, @tessera/collapsible, @tessera/dropdown-menu, @tessera/form, utils
PageShell
Das Geruest jeder Listen- und Werkzeugseite: Kopfzeile mit Brotkrumen und Navigation, Tabs mit URL-Sync und Werkzeugleisten-Registry, darunter die Inhaltsflaeche. Suche ist ein Slot.
Die Inhaltsfläche gehört der Page — hier stünde ein Grid.
Kopfzeile, Tabs und Werkzeugleiste sind Gerüst, kein Inhalt.
npx shadcn@latest add @tessera/page-shellZieht mit: @tessera/content-surface, @tessera/tabs, @tessera/breadcrumb, @tessera/dropdown-menu, @tessera/button, @tessera/badge, @tessera/sidebar, @tessera/form, utils
DetailPage
Vorlage einer Detailseite: Kopfzeile mit Nachbar-Navigation, Hero mit Bild, Titel, Status und Kennzahlen, Tab-Leiste, Slide-in-Panel fuer schmale Schirme.
skytess
rad:z3vhVW… — signierte Provenance
npx shadcn@latest add @tessera/detail-pageZieht mit: @tessera/page-shell, @tessera/button, @tessera/sheet, @tessera/scroll-area, @tessera/sidebar, @tessera/form, utils
CommandPalette
Eine Suche ueber alles, auf ⌘K: gruppierte Treffer aus mehreren Quellen, Tastaturnavigation, Hervorhebung und Verlauf. Eine tote Quelle nimmt die anderen nicht mit.
Nichts gewählt.
npx shadcn@latest add @tessera/command-paletteZieht mit: @tessera/dialog, @tessera/search-sources, @tessera/form, utils
SmartGrid
Die vollstaendige Liste: Suche, Facetten, Sortierung, Auswahl, Spaltenbreiten, Seitenwechsel und CSV-Export. Die Suche steckt hinter dem unified-search-Adapter — angeschlossen ist nichts.
Keine Treffer
npx shadcn@latest add @tessera/smart-gridZieht mit: @tessera/data-grid, @tessera/pagination, @tessera/select, @tessera/dropdown-menu, @tessera/button, @tessera/badge, @tessera/search-input, @tessera/search-syntax-hint, @tessera/csv-export, @tessera/unified-search, @tessera/form, utils
Bibliothek
actor-origin
isTesseraActor prueft mit endsWith auf .tessera.at — nie mit includes, sonst kann sich tessera.at.evil.example als heimisch ausgeben.
npx shadcn@latest add @tessera/actor-origincsv-export
CSV und ausschliesslich CSV. Quotet nach RFC 4180, entschaerft fuehrende Formelzeichen und schreibt die Bytefolgenmarkierung, damit Excel Umlaute liest.
npx shadcn@latest add @tessera/csv-exportsearch-query
Die Abfragesprache des Suchfelds: feld:wert, "exakte Phrase", -ausschluss und datum>2026-01. Zerlegt nur — gefunden wird woanders.
npx shadcn@latest add @tessera/search-querysearch-sources
Mehrere Quellen, eine Frage: fragt sie nebeneinander, gruppiert nach Herkunft und laesst eine tote Quelle die anderen nicht mitnehmen. Dazu die Hervorhebung der Treffer.
npx shadcn@latest add @tessera/search-sourcesunified-search
Die eine Schnittstelle, hinter der jede Suche steckt — Speicher, SQL oder Meilisearch. Mitgeliefert ist der Adapter fuer Listen, die schon da sind; angeschlossen ist nichts.
npx shadcn@latest add @tessera/unified-searchZieht mit: @tessera/search-query
Anmeldung mit Tessera
Der komplette OIDC-Login gegen id.tessera.at: Session als verschluesseltes Cookie (iron-session), Login-/Callback-/Logout-Routen mit PKCE-Transaktions-Cookie, einmaliger Selbstheilung und RP-initiiertem Logout. Kanonische Quelle ist start-simple.
npx shadcn@latest add @tessera/auth-oidcRegler — Schiene und bewegliches Teil
Kein Registry-Item — der Slider lebt bisher nur in dieser App. Schiene und bewegliches Teil tragen die Form einzeln; das Element selbst bleibt ungeclippt, ein Clip darauf würde den Griff an den Enden abschneiden. Die Goldfüllung braucht den Wert und kommt deshalb aus dem Slider-Atom, nicht aus dem CSS.
Die Regeln
- Die Form ist ein Token. Eine Definition in
app/tessera-form.css, überall angewandt — nicht pro Komponente nachgebaut. - Die Form ist farbunabhängig. Gold, Rot, Amber: gleiche Silhouette, andere Bedeutung.
- Fokus liegt innen. Im Browser gemessen: alles außerhalb der Form wird abgeschnitten — Umriss, Ring, Schatten.
- Ausnahmen sind benannt. Kästchen, Radios, Schalter, Avatare und schwebende Ebenen (Menü, Tooltip) behalten ihre Form und ihre Schatten.
- Das Zeichen wird nicht wiederholt. Es steht groß, wo es wirkt — Kopfzeile, Passkey-Schirm. Nicht in jedem Knopf: Auf der Startseite kam es sonst fünfmal vor. Die Marke trägt hier die Form.
ui/wird nie angefasst. Die Sprache greift über diedata-slot-Attribute, die shadcn selbst setzt.