Fill frontend rule gaps and broaden v2 tokens

Add the frontend guides the v2 UI kit and compliance-portal need but
that the first rework left uncovered: forms, routing, client state, and
permission-gated UI.

forms.md documents a tiered approach on Base UI Field/Form -- native
constraints, then a validate function, then zod parsed in onSubmit, and
react-hook-form only for large or dynamic forms -- and drops the custom
useFormWithSchema wrapper. routing.md covers @probo/routes, navigation,
typed params, URL-as-state, redirects, auth/protected routes, and the
folded-in no-outlet-context rule. state-management.md gives a decision
order across Relay, URL, local state, context, and zustand.
permissions.md gates UI on the canUpdate/canDelete permission(action:)
fields without re-encoding authorization in the client.

Rename v2-colors.md to v2-tokens.md and add the typography, radius,
shadow, and native-spacing scales alongside color. Extend ui.md with
user feedback, empty-state, and accessibility sections; standardize
toasts on Base UI's Toast (Toast.useToastManager) and retire the legacy
useToast across ui.md, forms.md, error-handling.md, and relay.md. Add an
Intl formatting section to i18n.md and a non-Relay HTTP / file
upload-download section to ts-style.md. Update the AGENTS.md index and
the v2-color-scale cursor rule for the new and renamed guides.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-23 19:01:38 +02:00
parent c158eb9be4
commit 393c538de1
15 changed files with 836 additions and 32 deletions

View File

@@ -120,27 +120,27 @@ Boundaries will not catch a rejected promise in a submit handler. Wrap the risky
```tsx
// Good — async event handler guards itself; the boundary above can't help here
function PublishButton() {
const { __ } = useTranslate();
const { toast } = useToast();
const { t } = useTranslation();
const toast = Toast.useToastManager();
const [isPublishing, setIsPublishing] = useState(false);
async function onPublish() {
setIsPublishing(true);
try {
await publishReport();
toast({ title: __("Published"), variant: "success" });
toast.add({ title: t("reports.published"), type: "success" });
} catch (error) {
toast({
title: __("Publish failed"),
description: error instanceof Error ? error.message : __("Unknown error"),
variant: "error",
toast.add({
title: t("reports.publishFailed"),
description: error instanceof Error ? error.message : t("common.unknownError"),
type: "error",
});
} finally {
setIsPublishing(false);
}
}
return <Button disabled={isPublishing} onClick={onPublish}>{__("Publish")}</Button>;
return <Button disabled={isPublishing} onClick={onPublish}>{t("reports.publish")}</Button>;
}
```