diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cddf6b..37ccfef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,52 @@ Migration. - Doku-Drift zum Capabilities-Endpunkt in CLAUDE.md und EXTRAKTIONSPLAN bereinigt. +## ui/v0.9.0 — 2026-10-11 + +### @basicbar/ui + +Anlass: abstimmbar #178 (Kontrast) brauchte einen Formularfeld-Rahmen mit +≥ 3:1 (WCAG 1.4.11) und Fehlertext mit ≥ 4.5:1 und überschrieb dafür per CSS +den Rahmen des Paket-Editors. Beides wird jetzt Paket-Funktion. + +- **Neue semantische Farben `field` und `danger`** im Preset (mit + `/ `, also z. B. `border-field/50`, `text-danger`). Sie lesen + die CSS-Variablen `--bb-field-border` und `--bb-danger` (OKLCH-Kanäle ohne + `oklch()`), deren Defaults `base.css` setzt (hell unter `:root`, dunkel unter + `:root.dark`). Defaults und berechnete Kontraste stehen als Kommentar in + `base.css`: `field` hell `0.6 0 0` (3.5–3.95:1 auf Weiß/slate-50/100), dunkel + `0.56 0 0` (≥ 3.76:1 auf slate-900/950); `danger` hell `0.55 0.235 27` + (≥ 4.74:1), dunkel `0.704 0.191 22.216` = red-400 (≥ 5.03:1). +- **Komponenten nutzen die Tokens:** Rahmen von `RichTextEditor` (Fokusring + unverändert) und Standard-Input von `TranslatableField` → `border-field` + (sichtbar etwas dunkler als vorher); Pflicht-Sternchen, Übersetzungsfehler und + „Pflichtfeld“-Meldung in `TranslatableField` sowie die Fehlermeldung der + Übersetzungsleiste → `text-danger` (vorher rose-500/600/400). +- **Preset-Kommentar korrigiert:** „slate-400 ≥ 4.5:1 auf Weiß“ galt nicht für + jede Rampe. Gedämpfter Text ist in beiden Themes gegen Seite und Karten auf + ≥ 4.5:1 zu prüfen. + +Tönen: Die Defaults sind neutral. Ein Tool färbt sie ein, indem es die +Variablen in seinem eigenen (ungeschichteten) CSS überschreibt, z. B. + +```css +:root { --bb-field-border: 0.62 0.011 220; --bb-danger: 0.55 0.235 27; } +:root.dark { --bb-field-border: 0.56 0.011 220; } +``` + +und die Kontraste (field ≥ 3:1, danger ≥ 4.5:1) gegen die eigenen Hintergründe +nachrechnet. + +Migration: + +1. Tarball auf `ui/v0.9.0`, Vite-Dep-Cache leeren. +2. Eigene Overrides des Editor-Rahmens (z. B. `.xy-rte > div.rounded-lg + { border-color: … }`) entfernen; stattdessen `--bb-field-border` setzen, + falls der Default nicht passt. Eine lokale `field`-Farbe im Tool-Preset + durch die Variable ersetzen. +3. Optional: lokale Fehlerrot-Muster (`text-red-600 dark:text-red-400` o. ä.) + durch `text-danger` ersetzen. + ## ui/v0.8.0 — 2026-10-10 ### @basicbar/ui diff --git a/packages/ui/README.md b/packages/ui/README.md index dd11bca..5a24cf6 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -12,6 +12,9 @@ Tools identisch aussehen, übergeben sie dieselbe Ramp. (Plus Jakarta Sans Variable), Dark Mode per `.dark`-Klasse, Motion-Tokens (`pop`, `fade-up`, `out-quart`). Die `slate`/`brand`-Ramps übergibt das Tool (OKLCH, Konventionen im Preset-Docstring). + Dazu die semantischen Farben `field` (Formularrahmen, ≥ 3:1) und `danger` + (Fehlertext, ≥ 4.5:1) über die Variablen `--bb-field-border`/`--bb-danger` + (Defaults in `base.css`, vom Tool überschreibbar). - **`@basicbar/ui/base.css`** — gemeinsame Basis-Styles (`@layer base` plus ungelayerte ProseMirror-Regeln): Canvas hell/dunkel, `accent-color`, `::selection`, konsistenter `:focus-visible`-Ring diff --git a/packages/ui/base.css b/packages/ui/base.css index 995284f..d605e01 100644 --- a/packages/ui/base.css +++ b/packages/ui/base.css @@ -16,9 +16,29 @@ `color-scheme` on ; these rules cover the pre-React first paint. */ :root { color-scheme: light; + /* Semantic colour tokens (Tailwind colours `field` and `danger`, see + tailwind-preset.js). Values are OKLCH channels without `oklch()` so the + preset can append `/ `. Neutral defaults: a tool tints + them by overriding the variables in its own (unlayered) CSS, e.g. + `:root { --bb-field-border: 0.6 0.011 220; }`, and re-checks the ratios. + Computed WCAG contrast (sRGB, clamped): + --bb-field-border (form-control boundary, WCAG 1.4.11 needs >=3:1) + light 0.6 0 0: 3.95 on white, 3.78 on slate-50, 3.54 on slate-100 + dark 0.56 0 0: 4.17 on slate-950/#090e10, 3.76 on slate-900, + 3.13 on slate-800 + --bb-danger (error text, WCAG 1.4.3 needs >=4.5:1) + light 0.55 0.235 27: 5.29 on white, 5.07 on slate-50, 4.74 on slate-100 + dark 0.704 0.191 22.216 (red-400): 6.7 on slate-950, 6.04 on slate-900, + 5.03 on slate-800 + (slate ramps of abstimmbar and ausleihbar; both give the same ratios to + within 0.01.) */ + --bb-field-border: 0.6 0 0; + --bb-danger: 0.55 0.235 27; } :root.dark { color-scheme: dark; + --bb-field-border: 0.56 0 0; + --bb-danger: 0.704 0.191 22.216; } /* Ink on white in light mode; a near-black canvas in dark mode. The accent diff --git a/packages/ui/package-lock.json b/packages/ui/package-lock.json index 7163739..081afff 100644 --- a/packages/ui/package-lock.json +++ b/packages/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@basicbar/ui", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@basicbar/ui", - "version": "0.8.0", + "version": "0.9.0", "license": "Apache-2.0", "dependencies": { "@tiptap/extension-bold": "^3.27.2", diff --git a/packages/ui/package.json b/packages/ui/package.json index b7c1f89..9b65461 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@basicbar/ui", - "version": "0.8.0", + "version": "0.9.0", "description": "Design-System-Basis der virtUOS -bar-Tools: Tailwind-Preset, Basis-Styles, Theme (Dark Mode), i18n-Bootstrap, contentLang, TranslatableField, Preferences, RichText; RichTextEditor (TipTap) als eigener Entry", "license": "Apache-2.0", "author": "Universität Osnabrück (virtUOS)", diff --git a/packages/ui/src/RichTextEditor.tsx b/packages/ui/src/RichTextEditor.tsx index c5a7e3e..1775c7c 100644 --- a/packages/ui/src/RichTextEditor.tsx +++ b/packages/ui/src/RichTextEditor.tsx @@ -313,7 +313,7 @@ export function RichTextEditor({ return (
diff --git a/packages/ui/src/TranslatableField.tsx b/packages/ui/src/TranslatableField.tsx index f9f6fb3..4494aab 100644 --- a/packages/ui/src/TranslatableField.tsx +++ b/packages/ui/src/TranslatableField.tsx @@ -61,7 +61,7 @@ export interface RenderInputArgs { } const DEFAULT_INPUT_CLASS = - "w-full rounded-lg border border-slate-300 dark:border-slate-700 bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:border-brand-600 focus:outline-none focus:ring-1 focus:ring-brand-600 dark:bg-slate-900 dark:text-slate-100"; + "w-full rounded-lg border border-field bg-white px-3 py-2 text-sm text-slate-900 placeholder:text-slate-400 focus:border-brand-600 focus:outline-none focus:ring-1 focus:ring-brand-600 dark:bg-slate-900 dark:text-slate-100"; export interface TranslatableFieldProps { /** Visible label above the field; omit for compact inline uses (the @@ -260,7 +260,7 @@ export function TranslatableField({ {labelAddon} @@ -384,14 +384,14 @@ export function TranslatableField({ )}
)} - {translateError &&

{translateError}

} + {translateError &&

{translateError}

} {hint && (

{hint}

)} {canonicalEmpty && ( -

+

{t("A value in {{language}} is required.", { language: defaultContentLangLabel(), })} diff --git a/packages/ui/src/TranslationForm.tsx b/packages/ui/src/TranslationForm.tsx index 33f3d2c..0c961ad 100644 --- a/packages/ui/src/TranslationForm.tsx +++ b/packages/ui/src/TranslationForm.tsx @@ -450,7 +450,7 @@ export function TranslationFormProvider({ const renderControls = (withHandle: boolean) => ( <> {error && ( - + {error} )} diff --git a/packages/ui/tailwind-preset.js b/packages/ui/tailwind-preset.js index 6ce74cc..3b6f8c0 100644 --- a/packages/ui/tailwind-preset.js +++ b/packages/ui/tailwind-preset.js @@ -11,7 +11,14 @@ * Ramp conventions (keep these when authoring a tool ramp, see the READMEs of * the existing tools; values are OKLCH and tuned for WCAG AA): * - `slate` is the neutral ramp, usually hue-tinted toward the accent - * (warm for honey, cool for green). slate-400 on white stays ≥4.5:1. + * (warm for honey, cool for green). Muted text (typically slate-400/500) + * must reach ≥4.5:1 on the page and card backgrounds in BOTH themes — a + * single ramp value often can't, so check light and dark and, if needed, + * drive it through a CSS variable that dark mode lifts. + * - Form-control borders and error text are NOT ramp steps: use the `field` + * and `danger` tokens below (variables `--bb-field-border` / `--bb-danger` + * with contrast-checked defaults in base.css; tools override the variables + * to tint them). * - `brand` is the single accent: CTAs use brand-400 with dark ink text, * brand-600 is the focus/selection color (≥3:1 non-text on white), * brand-700 is accent text on white (≥4.5:1). @@ -55,7 +62,13 @@ export function createPreset({ colors } = {}) { "sans-serif", ], }, - colors, + colors: { + // Semantic tokens; defaults live in base.css. Tools tint them by + // overriding the CSS variables, not by redefining `field`/`danger`. + field: "oklch(var(--bb-field-border) / )", + danger: "oklch(var(--bb-danger) / )", + ...colors, + }, transitionTimingFunction: { "out-quart": "cubic-bezier(0.25, 1, 0.5, 1)", },