mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
## What kind of change does this PR introduce? Accessibility cleanup (DEPR-628). ## What is the current behavior? Leftover call sites still use ad-hoc focus recipes (`ring-foreground-muted`, `outline-brand`, Dialog/Sheet `focus:` rings, etc.) instead of the shared utilities from #41575. ## What is the new behavior? Converts those leftovers across `packages/ui`, Studio, www, docs, and design-system to `focus-ring`, preferring `focus-visible`. Keeps documented exceptions (`group-focus-visible`, InputGroup `:has()`). ## To test Tab through controls (keyboard only). Expect a consistent offset ring on `:focus-visible`, not a green/brand/custom stack, and no ring animation. ### www (marketing) Preview: https://zone-www-dot-com-git-danny-depr-628-focus-ring-fbccf9-supabase.vercel.app - Global nav on `/`: Product, Developers, Solutions dropdowns; logo; hamburger + mobile menu - `/features`: view toggles and feature cards - `/company`: card links - `/changelog`: timeline / entry links - `/partners/catalog`: grid/list toggle and partner cards - `/pricing`: compute section expand control - Product / Modules / Solutions sticky navs on product pages (e.g. `/database`, `/storage`) - `/state-of-startups`: TwoOptionToggle if present ### docs Preview: https://docs-git-danny-depr-628-focus-ring-long-tail-supabase.vercel.app - Any guide page: top nav dropdowns and items - Narrow viewport: hamburger, then mobile menu links + close - Guide with PromptPanel / tabs: tab to prompt actions and tab list ### studio (dashboard) Preview: https://studio-staging-git-danny-depr-628-focus-ring-long-tail-supabase.vercel.app - Project home: Connect section tiles; drag-handle focus on sortable sections - Integrations marketplace (`/project/<ref>/integrations`): featured cards, list/grid toggle, list rows - Auth (`/project/<ref>/auth/oauth-apps`, `/project/<ref>/auth/providers`): open create/edit sheet, tab to close (X) - Database policies (`/project/<ref>/database/policies`): open policy editor sheet, tab to close - Storage policies (`/project/<ref>/storage/files/policies`): bucket section links; policy modal close - Query performance (`/project/<ref>/observability/query-performance`): info icon buttons on metrics - Replication pipeline detail (if available): slot lag / status info icons - Support (`/support/new`): attachment add/remove controls - Table editor: spreadsheet import preview checkboxes; row text/JSON editor TwoOptionToggle - Any Dialog/Sheet/toast close (X): ring on keyboard focus only, not mouse click ### design-system Preview: https://design-system-git-danny-depr-628-focus-ring-long-tail-supabase.vercel.app - Colour palette swatches (keyboard focus) - Form patterns sidepanel example: avatar / focusable control in the example ## Additional context - Linear: [DEPR-628](https://linear.app/supabase/issue/DEPR-628) - Follow-ups: form-group CSS (DEPR-629), Storage columns selection (DEPR-630), ESLint rule (DEPR-632) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Accessibility & Usability** * Standardized keyboard focus indicators across navigation, dialogs, forms, buttons, toggles, links, and tooltips using a consolidated focus style. * Improved toggle controls to use proper button semantics (instead of clickable text), including `aria-pressed`/disabled handling and better keyboard navigation. * **Visual Updates** * Harmonized hover/focus ring visuals across the design system, Studio, documentation, and marketing pages while preserving existing layout and interaction behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import { ArrowUpRight } from 'lucide-react'
|
|
import dynamic from 'next/dynamic'
|
|
import {
|
|
AiIconAnimation,
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
cn,
|
|
Skeleton,
|
|
} from 'ui'
|
|
|
|
import type { SubmittedSupportRequest } from './SupportForm.state'
|
|
import { NO_PROJECT_MARKER } from './SupportForm.utils'
|
|
|
|
interface SupportAssistantSuccessCardProps {
|
|
request: SubmittedSupportRequest
|
|
className?: string
|
|
}
|
|
|
|
const SupportAssistantSuccessCardContent = dynamic<SupportAssistantSuccessCardProps>(
|
|
() =>
|
|
import('@/components/ui/AIAssistantPanel/AIAssistant').then(
|
|
(mod) => mod.SupportAssistantSuccessCardContent
|
|
),
|
|
{
|
|
loading: () => <SupportAssistantSuccessCardLoadingShell />,
|
|
}
|
|
)
|
|
|
|
function hasProjectScopedAssistantContext(projectRef: string | undefined) {
|
|
return projectRef !== undefined && projectRef !== NO_PROJECT_MARKER
|
|
}
|
|
|
|
export function SupportAssistantSuccessCard(props: SupportAssistantSuccessCardProps) {
|
|
if (!hasProjectScopedAssistantContext(props.request.projectRef)) return null
|
|
|
|
return <SupportAssistantSuccessCardContent {...props} />
|
|
}
|
|
|
|
function SupportAssistantSuccessCardLoadingShell({ className }: { className?: string }) {
|
|
return (
|
|
<Card
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label="Open assistant response"
|
|
className={cn(
|
|
'group cursor-pointer bg-muted/50 transition-colors hover:bg-muted/50 focus-ring',
|
|
className
|
|
)}
|
|
>
|
|
<CardHeader className="flex-row items-center justify-between gap-4 space-y-0">
|
|
<div className="flex min-w-0 items-center gap-3">
|
|
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md border bg-background">
|
|
<AiIconAnimation size={14} />
|
|
</div>
|
|
<div className="min-w-0 space-y-1">
|
|
<CardTitle>While you wait</CardTitle>
|
|
<CardDescription>Assistant may be able to help</CardDescription>
|
|
</div>
|
|
</div>
|
|
<ArrowUpRight
|
|
size={14}
|
|
strokeWidth={1.5}
|
|
className="shrink-0 text-foreground-lighter transition-colors group-hover:text-foreground"
|
|
aria-hidden
|
|
/>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<SupportAssistantResponseLoadingSkeleton />
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function SupportAssistantResponseLoadingSkeleton() {
|
|
return (
|
|
<div className="space-y-2">
|
|
<Skeleton className="h-4 w-[82%]" />
|
|
<Skeleton className="h-4 w-[92%]" />
|
|
<Skeleton className="h-4 w-[68%]" />
|
|
</div>
|
|
)
|
|
}
|