fix(ui): Beschriftungszeile mit Diktatknopf richtet die Felder daneben aus

Im Abschnitt "Verlauf" der Opportunity begann "Was wurde besprochen?"
tiefer als "Letzte Interaktion" daneben. Ursache war die
Beschriftungszeile: rechts steht ein Diktatknopf darin, links nur Text -
die Zeile war damit 26,5 statt 16,5 Pixel hoch und schob das Eingabefeld
nach unten.

Der Knopf bekommt eine feste Hoehe (h-7, 28 px) statt sich aus
Zeilenhoehe, Innenabstand und Rahmen zu ergeben; auf 26,5 px laesst sich
keine Zeile ausrichten. Dazu labelRowClass mit min-h-7: damit ist die
Zeile in beiden Spalten gleich hoch, mit Knopf oder ohne. Gemessen sind
es jetzt 0,0 px Abweichung statt 8.

Zweiter Fund derselben Stelle: die drei Beschriftungen im Dialog waren
von Hand abgeschrieben statt labelClass zu nehmen. Damit fehlte ihnen die
Klasse "field-label", die seit den Themes die Schrift der
Feldbeschriftungen traegt - in "Pixel Art" blieben sie ohne
Pixelschrift. Die Auszeichnung steht jetzt einmal in leadUi.tsx
(labelTextClass) und wird nicht mehr kopiert; DictateTextarea benutzt
sie ebenso.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sascha Wolf
2026-09-09 15:30:35 +02:00
co-authored by Claude Opus 5
parent dc701f8522
commit ab6b9f2372
5 changed files with 50 additions and 21 deletions
+18 -3
View File
@@ -32,10 +32,25 @@ export const inputClass =
/*
* `field-label` ist keine Utility-Klasse, sondern ein Anker: darueber gibt ein
* Theme allen Feldbeschriftungen eine andere Schrift (siehe index.css). Ohne
* sie muesste jedes Label im Markup angefasst werden.
* sie muesste jedes Label im Markup angefasst werden - genau deshalb steht die
* Auszeichnung hier einmal und wird nicht abgeschrieben.
*/
export const labelClass =
'field-label mb-1.5 block text-[11px] font-extrabold uppercase tracking-wider text-ink-500';
export const labelTextClass =
'field-label text-[11px] font-extrabold uppercase tracking-wider text-ink-500';
/** Beschriftung als eigene Zeile - der Normalfall. */
export const labelClass = `${labelTextClass} mb-1.5 block`;
/**
* Beschriftungszeile mit Platz fuer einen Knopf rechts (Diktat).
*
* `min-h-7` ist der Punkt: der Diktatknopf ist 28 px hoch, eine nackte
* Beschriftung nur 16,5 px. Ohne die Mindesthoehe beginnt das Eingabefeld
* daneben ueber 10 px tiefer als das in der Nachbarspalte - im Abschnitt
* "Verlauf" der Opportunity war genau das zu sehen. Die Hoehe des Knopfes ist
* dafuer in DictationField.tsx auf denselben Wert festgelegt.
*/
export const labelRowClass = 'mb-1.5 flex min-h-7 items-center justify-between gap-3';
export const Avatar: React.FC<{
src?: string | null;
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react';
import { Modal } from '../ui/Modal';
import { LeadModal } from '../leads/LeadModal';
import { DictateTextarea, DictateButton, DictationHint } from '../ui/DictationField';
import { Avatar, inputClass, labelClass } from '../leads/leadUi';
import { Avatar, inputClass, labelClass, labelRowClass, labelTextClass } from '../leads/leadUi';
import { STAGE_COLORS, STAGE_HINTS, STAGE_LABELS } from './opportunityUi';
import { useToast } from '../Toast';
import { acquisitionOptionsApi, opportunitiesApi } from '../../lib/api';
@@ -590,9 +590,16 @@ export const OpportunityModal: React.FC<OpportunityModalProps> = ({
</h3>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div>
<label className={labelClass} htmlFor="opp-interaction">
Letzte Interaktion
</label>
{/*
Dieselbe Zeile wie beim Feld daneben (labelRowClass), obwohl hier
kein Knopf steht: sonst beginnt das Datumsfeld 8 px hoeher als
die Notiz rechts - der Diktatknopf macht die Nachbarzeile hoeher.
*/}
<div className={labelRowClass}>
<label className={labelTextClass} htmlFor="opp-interaction">
Letzte Interaktion
</label>
</div>
<input
id="opp-interaction"
type="date"
@@ -602,8 +609,8 @@ export const OpportunityModal: React.FC<OpportunityModalProps> = ({
/>
</div>
<div className="sm:col-span-2">
<div className="mb-1.5 flex items-center justify-between gap-3">
<label className="block text-[11px] font-extrabold uppercase tracking-wider text-ink-500" htmlFor="opp-interaction-note">
<div className={labelRowClass}>
<label className={labelTextClass} htmlFor="opp-interaction-note">
Was wurde besprochen?
</label>
<DictateButton
@@ -631,8 +638,8 @@ export const OpportunityModal: React.FC<OpportunityModalProps> = ({
</div>
<div className="mt-4">
<div className="mb-1.5 flex items-center justify-between gap-3">
<label className="block text-[11px] font-extrabold uppercase tracking-wider text-ink-500" htmlFor="opp-next">
<div className={labelRowClass}>
<label className={labelTextClass} htmlFor="opp-next">
Nächster Schritt
</label>
<DictateButton
+14 -8
View File
@@ -3,7 +3,7 @@ import { useRecorder } from '../../lib/dictation';
import { useWhisper } from '../../lib/whisper';
import { useNavigation } from '../../lib/navigation';
import { useToast } from '../Toast';
import { inputClass } from '../leads/leadUi';
import { inputClass, labelRowClass, labelTextClass } from '../leads/leadUi';
const MIC_ICON =
'M19 11a7 7 0 01-14 0m7 7v3m0-3a7 7 0 007-7m-7 7a7 7 0 01-7-7m7-9a3 3 0 013 3v6a3 3 0 01-6 0V5a3 3 0 013-3z';
@@ -52,7 +52,9 @@ export const DictateButton: React.FC<DictateButtonProps> = ({
goTo('Einstellungen', { tab: 'whisper' });
}}
title="Das Spracherkennungsmodell ist noch nicht geladen - hier einrichten"
className="flex items-center gap-1.5 rounded-lg border border-dashed border-ink-300 px-2 py-1 text-[11px] font-bold text-ink-400 transition hover:border-brand-400 hover:text-brand-700"
// Dieselbe Hoehe wie der Diktatknopf, damit die Beschriftungszeile in
// beiden Zustaenden gleich hoch bleibt.
className="flex h-7 items-center gap-1.5 rounded-lg border border-dashed border-ink-300 px-2 text-[11px] font-bold text-ink-400 transition hover:border-brand-400 hover:text-brand-700"
>
<svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={MIC_ICON} />
@@ -79,7 +81,14 @@ export const DictateButton: React.FC<DictateButtonProps> = ({
: `${label} diktieren`
}
aria-label={isRecording ? 'Aufnahme beenden' : `${label} diktieren`}
className={`flex items-center gap-1.5 rounded-lg border px-2 py-1 text-[11px] font-bold transition disabled:opacity-60 ${
/*
* `h-7` (28 px) statt sich aus Zeilenhoehe, Innenabstand und Rahmen zu
* ergeben: das ergab 26,5 px, und eine Beschriftungszeile laesst sich
* nicht auf einen halben Pixel ausrichten. Mit der runden Hoehe passt
* labelRowClass (min-h-7) genau, und das Feld daneben beginnt auf
* derselben Linie.
*/
className={`flex h-7 items-center gap-1.5 rounded-lg border px-2 text-[11px] font-bold transition disabled:opacity-60 ${
isRecording
? 'border-red-300 bg-red-50 text-red-700'
: 'border-ink-200 text-ink-500 hover:border-brand-300 hover:text-brand-700'
@@ -218,11 +227,8 @@ export const DictateTextarea: React.FC<DictateTextareaProps> = ({
onBeforeNavigate,
}) => (
<div>
<div className="mb-1.5 flex items-center justify-between gap-3">
<label
htmlFor={id}
className="block text-[11px] font-extrabold uppercase tracking-wider text-ink-500"
>
<div className={labelRowClass}>
<label htmlFor={id} className={labelTextClass}>
{label}
</label>
<DictateButton