frontend: space separated panels in settings frames

Add an opt-in `separated` prop to SettingsFrame/SettingsSection that adds the
COSS "Separated Panels" gap-4 between sibling panels (matches CardFrame's
built-in -1rem clip vars so rounded corners clip across the gap). Turn it on
for the multi-toggle frames — AI availability and the two notification
sections — so their panels no longer stack flush. Joined divide-y lists stay
flush.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-07-16 23:48:30 +03:00
parent 7312d15d67
commit 000591f87f
3 changed files with 22 additions and 2 deletions
@@ -211,6 +211,7 @@ export function AIPanel() {
{policy ? (
<SettingsFrame
description={t("settings.ai.availability.description")}
separated
title={t("settings.ai.availability.title")}
>
{isAdmin ? (
@@ -14,6 +14,7 @@ import {
CardFrameTitle,
} from "@/components/ui/card";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";
// A settings section rendered inside the COSS "frame" surface: a titled header
// above one or more cards.
@@ -34,15 +35,23 @@ export function SettingsFrame({
action,
children,
className,
separated = false,
}: {
title: string;
description?: string;
action?: ReactNode;
children: ReactNode;
className?: string;
/**
* COSS "Separated Panels": add a 1rem gap so sibling panels read as distinct
* cards instead of one flush joined list. The `gap-4` matches CardFrame's
* built-in `--clip-top/--clip-bottom: -1rem`, which keeps each panel's rounded
* corners clipping correctly across the gap. Leave off for a joined list.
*/
separated?: boolean;
}) {
return (
<CardFrame className={className}>
<CardFrame className={cn(separated && "gap-4", className)}>
<CardFrameHeader className="border-b border-border/60">
<CardFrameTitle className="text-base">{title}</CardFrameTitle>
{description ? (
@@ -63,14 +72,22 @@ export function SettingsSection({
description,
action,
children,
separated = false,
}: {
title: string;
description?: string;
action?: ReactNode;
children: ReactNode;
/** See {@link SettingsFrame}'s `separated` — spaces sibling panels apart. */
separated?: boolean;
}) {
return (
<SettingsFrame action={action} description={description} title={title}>
<SettingsFrame
action={action}
description={description}
separated={separated}
title={title}
>
{children}
</SettingsFrame>
);
@@ -339,6 +339,7 @@ export function ProfilePanel() {
<SettingsSection
description={t("settings.profile.patientNotificationsDescription")}
separated
title={t("settings.profile.patientNotifications")}
>
{patientNotifications.map((item) => (
@@ -356,6 +357,7 @@ export function ProfilePanel() {
<SettingsSection
description={t("settings.profile.accountNotificationsDescription")}
separated
title={t("settings.profile.accountNotifications")}
>
{accountNotifications.map((item) => (