Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
`/ <alpha-value>`, 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
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions packages/ui/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,29 @@
`color-scheme` on <html>; 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 `/ <alpha-value>`. 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
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -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)",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export function RichTextEditor({

return (
<div
className={`rounded-lg border border-slate-300 dark:border-slate-700 focus-within:ring-2 focus-within:ring-brand-600 focus-within:ring-offset-2 focus-within:ring-offset-white dark:focus-within:ring-offset-slate-950 ${
className={`rounded-lg border border-field focus-within:ring-2 focus-within:ring-brand-600 focus-within:ring-offset-2 focus-within:ring-offset-white dark:focus-within:ring-offset-slate-950 ${
editable ? "" : "bg-slate-50 dark:bg-slate-900"
}`}
>
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/TranslatableField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -260,7 +260,7 @@ export function TranslatableField({
<span className="flex items-center gap-1">
<label id={labelId} htmlFor={inputId} className={labelClassName}>
{label}
{required && <span className="text-rose-500"> *</span>}
{required && <span className="text-danger"> *</span>}
</label>
{labelAddon}
</span>
Expand Down Expand Up @@ -384,14 +384,14 @@ export function TranslatableField({
)}
</div>
)}
{translateError && <p className="mt-1 text-rose-500">{translateError}</p>}
{translateError && <p className="mt-1 text-danger">{translateError}</p>}
{hint && (
<p id={hintId} className="mt-1 text-slate-600 dark:text-slate-300">
{hint}
</p>
)}
{canonicalEmpty && (
<p id={requiredId} className="mt-1 text-rose-600 dark:text-rose-400">
<p id={requiredId} className="mt-1 text-danger">
{t("A value in {{language}} is required.", {
language: defaultContentLangLabel(),
})}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/TranslationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ export function TranslationFormProvider({
const renderControls = (withHandle: boolean) => (
<>
{error && (
<span className="rounded-md bg-white px-2 py-1 text-xs text-rose-600 shadow dark:bg-slate-800 dark:text-rose-400">
<span className="rounded-md bg-white px-2 py-1 text-xs text-danger shadow dark:bg-slate-800">
{error}
</span>
)}
Expand Down
17 changes: 15 additions & 2 deletions packages/ui/tailwind-preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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) / <alpha-value>)",
danger: "oklch(var(--bb-danger) / <alpha-value>)",
...colors,
},
transitionTimingFunction: {
"out-quart": "cubic-bezier(0.25, 1, 0.5, 1)",
},
Expand Down
Loading