Polish the locale mismatch banner layout

Use a full-bleed bar with clearer actions and a stacked
mobile layout so long language labels stay usable.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-21 16:04:57 +02:00
parent acd41b72f3
commit 199289b42b

View File

@@ -20,8 +20,8 @@
import { GlobeIcon, XIcon } from "@phosphor-icons/react"; import { GlobeIcon, XIcon } from "@phosphor-icons/react";
import { Button } from "@probo/ui/src/v2/Button/Button"; import { Button } from "@probo/ui/src/v2/Button/Button";
import { Callout } from "@probo/ui/src/v2/Callout/Callout";
import { IconButton } from "@probo/ui/src/v2/IconButton/IconButton"; import { IconButton } from "@probo/ui/src/v2/IconButton/IconButton";
import { Text } from "@probo/ui/src/v2/typography/Text";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { graphql, useFragment } from "react-relay"; import { graphql, useFragment } from "react-relay";
@@ -46,8 +46,8 @@ interface LocaleMismatchCalloutProps {
identityKey: LocaleMismatchCallout_identity$key; identityKey: LocaleMismatchCallout_identity$key;
} }
// Soft notice when the URL locale differs from the signed-in identity preference. // Full-bleed notice when the URL locale differs from the signed-in identity
// Dismissed state is React-only (no localStorage/cookies). // preference. Dismissed state is React-only (no localStorage/cookies).
export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProps) { export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const identity = useFragment(localeMismatchCalloutFragment, identityKey); const identity = useFragment(localeMismatchCalloutFragment, identityKey);
@@ -75,45 +75,61 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
}; };
return ( return (
<div className="border-b border-sand-6 bg-sand-2 px-4 py-3"> <aside
<Callout size={1} variant="soft" color="gold" icon={<GlobeIcon weight="fill" />}> className="flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3"
<div className="flex flex-wrap items-center gap-3"> role="status"
<p className="min-w-0 flex-1 text-2 text-sand-12"> >
{t("locale.mismatch.message", { language: urlLabel })} <div className="flex min-w-0 flex-1 items-start gap-2">
</p> <GlobeIcon weight="fill" className="mt-0.5 size-4 shrink-0 text-gold-11" aria-hidden />
<div className="flex flex-wrap items-center gap-2"> <Text size={2} color="neutral" highContrast className="min-w-0 flex-1">
<Button {t("locale.mismatch.message", { language: urlLabel })}
size={1} </Text>
variant="soft" <IconButton
color="neutral" size={1}
disabled={busy} variant="ghost"
onClick={switchToSaved} color="neutral"
> aria-label={t("locale.mismatch.dismiss")}
{t("locale.mismatch.switchToMine", { language: savedLabel })} disabled={busy}
</Button> className="md:hidden"
<Button onClick={() => setDismissed(true)}
size={1} >
variant="solid" <XIcon />
color="neutral" </IconButton>
highContrast </div>
disabled={busy} <div className="flex shrink-0 items-center gap-2 max-md:flex-col max-md:items-stretch">
onClick={adoptUrlLocale} <Button
> size={1}
{t("locale.mismatch.useThis", { language: urlLabel })} variant="solid"
</Button> color="gold"
<IconButton disabled={busy}
size={1} className="max-md:w-full"
variant="ghost" onClick={switchToSaved}
color="neutral" >
aria-label={t("locale.mismatch.dismiss")} {t("locale.mismatch.switchToMine", { language: savedLabel })}
disabled={busy} </Button>
onClick={() => setDismissed(true)} <Button
> size={1}
<XIcon /> variant="solid"
</IconButton> color="neutral"
</div> highContrast
</div> disabled={busy}
</Callout> className="max-md:w-full"
</div> onClick={adoptUrlLocale}
>
{t("locale.mismatch.useThis", { language: urlLabel })}
</Button>
<IconButton
size={1}
variant="ghost"
color="neutral"
aria-label={t("locale.mismatch.dismiss")}
disabled={busy}
className="max-md:hidden"
onClick={() => setDismissed(true)}
>
<XIcon />
</IconButton>
</div>
</aside>
); );
} }