fix: Beute-Editor fängt den Fokus, SVG-Icon im Kopf

- Auf /colonies/[id]/hive bekommen Sprunglink, Seitenleiste, Kopf,
  Offline-Band, untere Navigation und „+ Neu“ das Attribut inert; der
  erste Tab landet auf „Zurück“ im Editor (vorher Stopp 34).
- metadata.icons verlinkt /icon.svg neben favicon.ico und apple-icon.
- Tests: alle THEME_COLORS gleich dem jeweiligen --background,
  ausdrücklich gewähltes Dunkel färbt die theme-color.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick.RosenloecherandClaude Opus 5.5 committed 2026-10-16 18:20:28 +02:00
1 parent ce68729233
commit 6ac9eef93b
11 files changed
+221 -9

No files matched your search

@@ -103,6 +103,28 @@ describe("appearanceScript", () => {
}
});
it("an explicit dark choice paints both media theme-color metas dark, on a light device too", () => {
stubMedia(false, false);
const metas = (["light", "dark"] as const).map((scheme) => {
const meta = document.createElement("meta");
meta.name = "theme-color";
meta.media = `(prefers-color-scheme: ${scheme})`;
meta.content = scheme === "light" ? "#f9f4ea" : "#1a120d";
document.head.append(meta);
return meta;
});
window.localStorage.setItem(APPEARANCE_KEYS.theme, "dark");
run(appearanceScript());
expect(metas.map((meta) => meta.content)).toEqual(["#1a120d", "#1a120d"]);
// Light is the default (dark only on request): a dark device without a choice stays light.
window.localStorage.clear();
stubMedia(true, false);
run(appearanceScript());
expect(metas.map((meta) => meta.content)).toEqual(["#f9f4ea", "#f9f4ea"]);
});
it("has no build flag for dark any more (task A4)", () => {
expect(appearanceScript()).not.toMatch(/DARK=/);
});
@@ -34,11 +34,11 @@ function setOnLine(value: boolean) {
}
/** The band as the layout places it: above the page content wrapper. */
function BandOnPage({ syncEnabled }: { syncEnabled?: boolean }) {
function BandOnPage({ syncEnabled, inert }: { syncEnabled?: boolean; inert?: boolean }) {
const pageContentRef = useRef<HTMLDivElement>(null);
return (
<>
<OfflineBand userId="user-1" pageContentRef={pageContentRef} syncEnabled={syncEnabled} />
<OfflineBand userId="user-1" pageContentRef={pageContentRef} syncEnabled={syncEnabled} inert={inert} />
<div ref={pageContentRef} tabIndex={-1} data-testid="page-content">
Seiteninhalt
</div>
@@ -46,7 +46,7 @@ function BandOnPage({ syncEnabled }: { syncEnabled?: boolean }) {
);
}
function renderBand(props: { syncEnabled?: boolean } = {}) {
function renderBand(props: { syncEnabled?: boolean; inert?: boolean } = {}) {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return render(
<QueryClientProvider client={client}>
@@ -85,6 +85,19 @@ describe("OfflineBand", () => {
vi.restoreAllMocks();
});
it("leaves the tab order and the accessibility tree while a full-screen page covers it", async () => {
setOnLine(false);
vi.mocked(summarizeQueue).mockResolvedValue({ own: 2, failed: 0, foreign: 0 });
const { unmount } = renderBand({ inert: true });
await waitFor(() => expect(summarizeQueue).toHaveBeenCalled());
expect(document.querySelector('[aria-live="polite"]')).toHaveAttribute("inert");
unmount();
renderBand();
expect(screen.getByRole("status")).not.toHaveAttribute("inert");
});
it("announces „Offline – 2 Einträge warten\" in a status region", async () => {
setOnLine(false);
vi.mocked(summarizeQueue).mockResolvedValue({ own: 2, failed: 0, foreign: 0 });
@@ -38,6 +38,8 @@ interface OfflineBandProps {
* nor sends - the server would only answer 401.
*/
syncEnabled?: boolean;
/** True while a full-screen page covers the shell: out of the tab order and the accessibility tree. */
inert?: boolean;
}
/**
@@ -45,7 +47,7 @@ interface OfflineBandProps {
* device, how much still waits to be sent and what the server rejected.
* Sends by itself as soon as the connection is back.
*/
export function OfflineBand({ userId, pageContentRef, syncEnabled = true }: OfflineBandProps) {
export function OfflineBand({ userId, pageContentRef, syncEnabled = true, inert }: OfflineBandProps) {
const queryClient = useQueryClient();
const connectivity = useConnectivity();
const statusRef = useRef<HTMLDivElement>(null);
@@ -136,6 +138,7 @@ export function OfflineBand({ userId, pageContentRef, syncEnabled = true }: Offl
role="status"
aria-live="polite"
tabIndex={-1}
inert={inert}
className={cn(
hasContent &&
"flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-border/60 px-4 py-2 text-sm sm:px-6",