Skip to content
Open
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
9 changes: 4 additions & 5 deletions app/(dashboard)/sse/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
type ConfigFormState,
} from "@/lib/sse/config"
import { RekeyCard } from "@/components/sse/rekey-card"
import { DANGER_BUTTON_CLASS } from "@/components/danger-button"
import type {
KmsBackendCapabilities,
KmsConfigPayload,
Expand Down Expand Up @@ -2263,7 +2264,7 @@ export default function SSEPage() {
<AlertDialogAction
onClick={confirmServiceAction}
disabled={startingKMS || stoppingKMS || Boolean(activeMutation) || Boolean(statusError)}
className={pendingServiceAction === "stop" ? "bg-destructive text-destructive-foreground" : undefined}
className={pendingServiceAction === "stop" ? DANGER_BUTTON_CLASS : undefined}
>
{startingKMS || stoppingKMS ? <Spinner className="size-4" /> : null}
{pendingServiceAction === "stop" ? t("Stop KMS") : t("Confirm")}
Expand All @@ -2290,7 +2291,7 @@ export default function SSEPage() {
<AlertDialogAction
onClick={discardConfigChangesAndNavigate}
disabled={mutationInFlight}
className="bg-destructive text-destructive-foreground"
className={DANGER_BUTTON_CLASS}
>
{t("Discard")}
</AlertDialogAction>
Expand Down Expand Up @@ -2329,9 +2330,7 @@ export default function SSEPage() {
Boolean(statusError) ||
Boolean(isPendingDefaultKey && pendingKeyAction?.type !== "cancelDeletion")
}
className={
pendingKeyAction?.type === "forceDelete" ? "bg-destructive text-destructive-foreground" : undefined
}
className={pendingKeyAction?.type === "forceDelete" ? DANGER_BUTTON_CLASS : undefined}
>
{processingKeyAction ? <Spinner className="size-4" /> : null}
{pendingKeyAction?.type === "cancelDeletion"
Expand Down
26 changes: 24 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
Expand All @@ -43,12 +44,22 @@
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--font-heading: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, monospace;
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", var(--font-fallback), sans-serif;
--font-heading: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, var(--font-fallback), monospace;
--font-mono:
ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
var(--font-fallback), monospace;
}

:root {
color-scheme: light;
--font-latin: "Noto Sans", "DejaVu Sans", "Liberation Sans";
--font-han:
"PingFang SC", "Microsoft YaHei", "Noto Sans SC", "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Micro Hei";
--font-kana: "Hiragino Sans", "Yu Gothic", "Meiryo", "Noto Sans JP", "Noto Sans CJK JP";
--font-hangul: "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", "Noto Sans CJK KR";
--font-arabic: "Noto Sans Arabic", "Noto Naskh Arabic", "Geeza Pro", "Tahoma";
--font-fallback: var(--font-latin), var(--font-han), var(--font-kana), var(--font-hangul), var(--font-arabic);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
Expand All @@ -62,6 +73,7 @@
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
Expand All @@ -83,6 +95,14 @@
--foreground: oklch(0.145 0 0);
}

:root:lang(ja) {
--font-fallback: var(--font-latin), var(--font-kana), var(--font-han), var(--font-hangul), var(--font-arabic);
}

:root:lang(ko) {
--font-fallback: var(--font-latin), var(--font-hangul), var(--font-kana), var(--font-han), var(--font-arabic);
}

.dark {
color-scheme: dark;
--background: oklch(0.145 0 0);
Expand All @@ -100,6 +120,8 @@
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
/* Dark-theme destructive is lightened for tinted text, so its solid fills need a dark label. */
--destructive-foreground: oklch(0.145 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
Expand Down
14 changes: 14 additions & 0 deletions components/danger-button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Solid danger styling for buttons that commit an irreversible action.
*
* The `destructive` Button variant only tints the background (`bg-destructive/10`
* with a `text-destructive` label), so it cannot carry a filled label. Buttons
* that confirm a destructive action layer this class on top of the base button
* classes instead, and read the label from `--destructive-foreground`.
*
* That token is a near-white on the light-theme red (4.6:1) and a near-black on
* the lightened dark-theme red (6.9:1), so the label clears WCAG AA in both
* themes. Override the fill or the label, never the two independently.
*/
export const DANGER_BUTTON_CLASS =
"bg-destructive text-destructive-foreground hover:bg-destructive/85 focus-visible:border-destructive/40 focus-visible:ring-destructive/30"
5 changes: 3 additions & 2 deletions components/dialog-host.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { useDialogController } from "@/lib/feedback/dialog"
import type { DialogInstance } from "@/lib/feedback/dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { DANGER_BUTTON_CLASS } from "@/components/danger-button"

function positiveButtonVariant(dialog: DialogInstance) {
return dialog.tone === "destructive" ? "destructive" : dialog.tone === "warning" ? "secondary" : "default"
return dialog.tone === "warning" ? "secondary" : "default"
}

export function DialogHost() {
Expand Down Expand Up @@ -79,7 +80,7 @@ export function DialogHost() {
className={cn(
buttonVariants({ variant: positiveButtonVariant(dialog) }),
"w-full sm:w-auto",
positiveButtonVariant(dialog) === "destructive" && "text-white",
dialog.tone === "destructive" && DANGER_BUTTON_CLASS,
)}
onClick={(e) => {
e.preventDefault()
Expand Down
5 changes: 3 additions & 2 deletions components/object/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import { SearchInput } from "@/components/search-input"
import { PageHeader } from "@/components/page-header"
import { DataTable } from "@/components/data-table/data-table"
import { DANGER_BUTTON_CLASS } from "@/components/danger-button"
import { Spinner } from "@/components/ui/spinner"
import { useDataTable } from "@/hooks/use-data-table"
import { useObject } from "@/hooks/use-object"
Expand All @@ -61,6 +62,7 @@ import { exportFile } from "@/lib/export-file"
import { getAttachmentContentDisposition } from "@/lib/content-disposition"
import { getContentType } from "@/lib/mime-types"
import { formatBytes, formatDateTime } from "@/lib/functions"
import { cn } from "@/lib/utils"
import { normalizeDateToIso } from "@/lib/safe-date"
import { buildBucketPath } from "@/lib/bucket-path"
import {
Expand Down Expand Up @@ -1086,8 +1088,7 @@ export function ObjectList({
<AlertDialogAction
render={
<Button
variant="destructive"
className="w-full text-white sm:w-auto"
className={cn("w-full sm:w-auto", DANGER_BUTTON_CLASS)}
onClick={handleConfirmDelete}
disabled={bucketVersioningState === "unknown"}
>
Expand Down
Loading