chore(a11y): convert leftover focus recipes to focus-ring (#48219)

## 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 -->
This commit is contained in:
Danny White authored and GitHub committed 2026-07-23 08:52:22 +10:00
1 parent 0bef8e7d90
commit 3bca21b3f8
53 files changed
+142 -142

No files matched your search

@@ -64,7 +64,7 @@ const ColorPalette = () => {
key={step * 100}
type="button"
onClick={() => handleCopy(reference)}
className="group relative flex aspect-square w-full items-center justify-center rounded-sm border border-overlay/40 transition hover:scale-[1.05] focus:outline-none focus-visible:ring-2 focus-visible:ring-foreground"
className="group relative flex aspect-square w-full items-center justify-center rounded-sm border border-overlay/40 transition-transform hover:scale-[1.05] focus-ring"
style={{ backgroundColor: reference }}
title={reference}
>
@@ -319,7 +319,7 @@ export default function FormPatternsSidePanel() {
tabIndex={0}
type="button"
onClick={() => uploadButtonRef.current?.click()}
className="flex items-center justify-center h-10 w-10 shrink-0 text-foreground-lighter hover:text-foreground-light overflow-hidden rounded-full bg-cover border hover:border-strong focus-visible:outline-brand-600"
className="flex items-center justify-center h-10 w-10 shrink-0 text-foreground-lighter hover:text-foreground-light overflow-hidden rounded-full bg-cover border hover:border-strong focus-ring"
style={{
backgroundImage: logoUrl ? `url("${logoUrl}")` : 'none',
}}
@@ -29,7 +29,7 @@ const listItem = {
}
const itemClassName =
'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:rounded-sm'
'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-ring rounded-sm'
const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => {
const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS)
@@ -145,7 +145,7 @@ const GlobalMobileMenu = ({ open, setOpen }: Props) => {
tabIndex={0}
onClick={() => setOpen(false)}
type="button"
className="inline-flex items-center justify-center focus:ring-brand bg-surface-100 hover:bg-surface-200 focus:outline-hidden focus:ring-2 focus:ring-inset border border-default bg-surface-100/75 text-foreground-light rounded-sm min-w-[30px] w-[30px] h-[30px]"
className="inline-flex items-center justify-center bg-surface-100 hover:bg-surface-200 border border-default bg-surface-100/75 text-foreground-light rounded-sm min-w-[30px] w-[30px] h-[30px] focus-ring"
>
<span className="sr-only">Close menu</span>
<X />
@@ -59,7 +59,7 @@ export const useActiveMenuLabel = (menu: typeof GLOBAL_MENU_ITEMS) => {
const GlobalNavigationMenu: FC = () => {
const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS)
const triggerClassName =
'h-(--header-height) p-2 bg-transparent border-0 border-b-2 border-transparent font-normal rounded-none text-foreground-light hover:bg-transparent hover:text-foreground data-open:bg-transparent! data-open:text-foreground! data-radix-collection-item:focus-visible:ring-2 data-radix-collection-item:focus-visible:ring-foreground-lighter data-radix-collection-item:focus-visible:text-foreground h-full focus-visible:rounded-sm shadow-none! outline-hidden transition-all outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-brand-600'
'h-(--header-height) p-2 bg-transparent border-0 border-b-2 border-transparent font-normal rounded-none text-foreground-light hover:bg-transparent hover:text-foreground data-open:bg-transparent! data-open:text-foreground! focus-ring focus-visible:text-foreground h-full focus-visible:rounded-sm shadow-none!'
return (
<div className="flex relative gap-2 justify-start items-end w-full h-full">
@@ -172,7 +172,7 @@ export const MenuItem = React.forwardRef<
ref={ref}
className={cn(
'group/menu-item flex items-center gap-2',
'w-full flex h-8 items-center text-foreground-light text-sm hover:text-foreground select-none rounded-md p-2 leading-none no-underline outline-hidden! focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground',
'w-full flex h-8 items-center text-foreground-light text-sm hover:text-foreground select-none rounded-md p-2 leading-none no-underline focus-ring focus-visible:text-foreground',
className
)}
{...props}
+2 -2
View File
@@ -133,7 +133,7 @@ function ExpandableContent({ children }: { children: ReactNode }) {
tabIndex={0}
type="button"
onClick={() => setIsExpanded((expanded) => !expanded)}
className="mt-2 text-sm text-brand-link transition-colors hover:text-brand focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-foreground-muted"
className="mt-2 text-sm text-brand-link transition-colors hover:text-brand focus-ring"
aria-expanded={isExpanded}
>
{isExpanded ? 'Show less' : 'Show more'}
@@ -192,7 +192,7 @@ function CopyButton({ label, value }: { label: string; value: string }) {
setCopied(true)
})
}}
className="rounded-sm p-1.5 text-foreground-muted transition-colors hover:bg-surface-200 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-foreground-muted"
className="rounded-sm p-1.5 text-foreground-muted transition-colors hover:bg-surface-200 hover:text-foreground focus-ring"
aria-label={copied ? `${label} copied` : `Copy ${label}`}
title={copied ? 'Copied' : 'Copy to clipboard'}
>
+1 -1
View File
@@ -55,7 +55,7 @@ export const tabsListVariants = cva(cn('flex'), {
export const tabsTriggerListVariants = cva(
cn(
'relative cursor-pointer flex items-center space-x-2 text-center transition focus:outline-hidden focus-visible:ring-3 focus-visible:ring-foreground-muted focus-visible:border-foreground-muted'
'relative cursor-pointer flex items-center space-x-2 text-center transition-colors focus-ring'
),
{
variants: {
@@ -260,10 +260,8 @@ export const CreateOrUpdateCustomProviderSheet = ({
<div className="flex flex-row gap-3 items-center">
<SheetClose
className={cn(
'text-muted hover:text ring-offset-background transition-opacity hover:opacity-100',
'focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',
'disabled:pointer-events-none data-[state=open]:bg-secondary',
'transition'
'text-muted hover:text hover:opacity-100 focus-ring',
'disabled:pointer-events-none data-[state=open]:bg-secondary'
)}
>
<X className="h-3 w-3" />
@@ -263,10 +263,8 @@ export const CreateOrUpdateOAuthAppSheet = ({
<div className="flex flex-row gap-3 items-center">
<SheetClose
className={cn(
'text-muted hover:text ring-offset-background transition-opacity hover:opacity-100',
'focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',
'disabled:pointer-events-none data-[state=open]:bg-secondary',
'transition'
'text-muted hover:text hover:opacity-100 focus-ring',
'disabled:pointer-events-none data-[state=open]:bg-secondary'
)}
>
<X className="h-3 w-3" />
@@ -30,9 +30,8 @@ export const PolicyEditorPanelHeader = ({
<div className="flex flex-row gap-3 max-w-[75%] items-start">
<SheetClose
className={cn(
'text-muted hover:text ring-offset-background transition-opacity hover:opacity-100',
'focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',
'transition disabled:pointer-events-none data-[state=open]:bg-secondary',
'text-muted hover:text hover:opacity-100 focus-ring',
'disabled:pointer-events-none data-[state=open]:bg-secondary',
'mt-1.5'
)}
>
@@ -135,7 +135,7 @@ export const SlotLagMetricsList = ({
type="button"
tabIndex={0}
aria-label={`What is ${field.label}`}
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-foreground-lighter"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-ring"
>
<Info size={12} />
</button>
@@ -66,7 +66,7 @@ export const SlotStatusLegend = () => {
type="button"
tabIndex={0}
aria-label="What do the slot statuses mean?"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-foreground-lighter"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-ring"
>
<Info size={12} />
</button>
@@ -18,7 +18,7 @@ export const MarketplaceCard = ({ integration, isInstalled }: MarketplaceCardPro
return (
<Link
href={`/project/${ref}/integrations/${integration.id}/overview`}
className="rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background"
className="rounded-md focus-ring"
>
<Card className="flex min-h-[168px] h-full flex-col gap-2.5 hover:border-stronger p-4">
<div className="flex items-start justify-between">
@@ -113,7 +113,7 @@ export const MarketplaceFeaturedHeroGrid = ({
<div className="col-span-1 @xl:col-span-2">
<Link
href={`/project/${ref}/integrations/${primaryIntegration.id}/overview`}
className="block h-full rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background"
className="block h-full rounded-md focus-ring"
>
<Card className="relative flex flex-row overflow-hidden h-full min-h-[168px] hover:border-stronger">
<div className="relative z-10 flex flex-col gap-2.5 p-4 flex-1 min-w-0">
@@ -170,7 +170,7 @@ export const MarketplaceFeaturedHeroGrid = ({
<div key={integration.id} className="col-span-1">
<Link
href={`/project/${ref}/integrations/${integration.id}/overview`}
className="block h-full rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background"
className="block h-full rounded-md focus-ring"
>
<Card className="flex flex-col overflow-hidden h-full hover:border-stronger">
<div className="hidden @xl:block relative w-full h-28 bg-black/90 dark:bg-black/50 shrink-0">
@@ -220,7 +220,7 @@ export const MarketplaceFilterBar = ({
onClick={() => onViewModeChange('grid')}
className={cn(
'border-r px-2 py-1.5 rounded-l-md cursor-pointer',
'focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-muted focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'focus-visible:z-10 focus-ring',
viewMode === 'grid'
? 'bg-surface-200 text-foreground'
: 'text-foreground-light hover:bg-surface-100'
@@ -235,7 +235,7 @@ export const MarketplaceFilterBar = ({
onClick={() => onViewModeChange('list')}
className={cn(
'px-2 py-1.5 rounded-r-md cursor-pointer',
'focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-muted focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'focus-visible:z-10 focus-ring',
viewMode === 'list'
? 'bg-surface-200 text-foreground'
: 'text-foreground-light hover:bg-surface-100'
@@ -33,12 +33,9 @@ export const MarketplaceListRow = ({ integration, isInstalled }: MarketplaceList
</TableCell>
<TableCell>
<Link
href={href}
className="block w-full hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background rounded-md"
>
<Link href={href} className="block w-full rounded-md hover:underline focus-ring">
<div className="flex flex-wrap items-center gap-x-1.5 gap-y-1">
<div className="relative z-10 text-sm font-medium @lg:text-sm after:absolute after:-inset-y-2 after:left-0 after:right-[calc(100%+6px)] after:content-[''] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-foreground">
<div className="relative z-10 text-sm font-medium @lg:text-sm after:absolute after:-inset-y-2 after:left-0 after:right-[calc(100%+6px)] after:content-['']">
{integration.name}
</div>
<div className="relative z-10">
@@ -92,7 +92,7 @@ export const ConnectSection = () => {
onClick={() => handleActionClick(action)}
className={cn(
'group flex items-center gap-3 p-4 text-left transition-colors min-h-[72px] w-full',
'hover:bg-surface-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-brand',
'hover:bg-surface-100 focus-ring',
'xl:min-h-32 xl:flex-col xl:justify-center xl:p-6 xl:text-center',
((action.requiresActiveProject ?? true)
? !isActiveHealthy
@@ -27,8 +27,7 @@ export const SortableSection = ({ id, children }: SortableSectionProps) => {
aria-label="Drag to reorder section"
className={cn(
'absolute -left-6 top-2 text-foreground-muted hover:text-foreground cursor-grab active:cursor-grabbing',
'rounded-sm outline-hidden',
'focus-visible:outline-solid focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-border-strong'
'rounded-sm focus-ring'
)}
{...attributes}
{...listeners}
@@ -71,7 +71,7 @@ export const QueryPerformanceMetrics = () => {
type="button"
tabIndex={0}
aria-label="How are slow queries calculated?"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-foreground-lighter"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-ring"
onClick={(e) => {
e.stopPropagation()
}}
@@ -92,7 +92,7 @@ export const QueryPerformanceMetrics = () => {
type="button"
tabIndex={0}
aria-label={`What is ${card.title}?`}
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-foreground-lighter"
className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-ring"
onClick={(e) => {
e.stopPropagation()
}}
@@ -72,7 +72,7 @@ export const BucketsPolicies = ({
aria-label="Toggle bucket list"
className={cn(
'rounded-md p-1 text-foreground-light hover:text-foreground',
'outline-hidden focus-visible:ring-2 focus-visible:ring-foreground-muted focus-visible:ring-offset-1 focus-visible:ring-offset-background'
'focus-ring'
)}
>
<ChevronUp size={14} className={cn(!expanded && 'rotate-180', 'transition')} />
@@ -32,7 +32,7 @@ export const StoragePolicyEditorModalTitle = ({
tabIndex={0}
onClick={onSelectBackFromTemplates}
className={cn(
'cursor-pointer rounded-xs opacity-20 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'cursor-pointer rounded-xs opacity-20 hover:opacity-100 focus-ring disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'hit-area-6'
)}
>
@@ -51,7 +51,7 @@ export const StoragePolicyEditorModalTitle = ({
<DocsButton href={`${DOCS_URL}/learn/auth-deep-dive/auth-policies`} />
<DialogPrimitive.Close
className={cn(
'absolute p-0.5 right-3.5 top-4.5 rounded-xs opacity-20 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'absolute p-0.5 right-3.5 top-4.5 rounded-xs opacity-20 hover:opacity-100 focus-ring disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'hit-area-6'
)}
>
@@ -24,7 +24,7 @@ const MAX_ATTACHMENTS = 5
const removeAttachmentButtonClassName = cn(
'absolute -top-1 -right-1 size-4 shrink-0 rounded-full bg-red-900 p-0 cursor-pointer',
'outline-hidden focus-visible:ring-2 focus-visible:ring-foreground-muted focus-visible:ring-offset-1 focus-visible:ring-offset-background'
'focus-ring'
)
const RemoveAttachmentIcon = () => (
@@ -257,9 +257,9 @@ export function AttachmentUploadDisplay({
tabIndex={0}
aria-label="Add attachment"
className={cn(
'border border-stronger opacity-50 transition hover:opacity-100',
'border border-stronger opacity-50 hover:opacity-100',
'group flex h-14 w-14 cursor-pointer items-center justify-center rounded-sm',
'outline-hidden focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-foreground-muted focus-visible:ring-offset-1 focus-visible:ring-offset-background'
'focus-visible:opacity-100 focus-ring'
)}
onClick={addFile}
>
@@ -46,7 +46,7 @@ function SupportAssistantSuccessCardLoadingShell({ className }: { className?: st
tabIndex={0}
aria-label="Open assistant response"
className={cn(
'group cursor-pointer bg-muted/50 transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand',
'group cursor-pointer bg-muted/50 transition-colors hover:bg-muted/50 focus-ring',
className
)}
>
@@ -179,7 +179,7 @@ export const SpreadsheetImportPreview = ({
<button
type="button"
tabIndex={0}
className="flex items-center space-x-2 cursor-pointer focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary"
className="flex cursor-pointer items-center space-x-2 focus-ring"
onClick={() => onSelectExpandError(key)}
aria-expanded={isExpanded}
aria-controls={`${key}-panel`}
@@ -135,7 +135,7 @@ export function SupportAssistantSuccessCardContent({
}
}}
className={cn(
'group cursor-pointer bg-muted/50 transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand',
'group cursor-pointer bg-muted/50 transition-colors hover:bg-muted/50 focus-ring',
className
)}
>
+13 -7
View File
@@ -24,13 +24,13 @@ export const TwoOptionToggle = ({
) => `absolute top-0 z-1 text-xs inline-flex h-full items-center justify-center font-medium
${
isActive ? 'hover:text-foreground-light hover:text-foreground' : 'hover:text-foreground'
} hover:text-foreground focus:z-10 focus:outline-hidden focus:border-blue-300 focus:ring-blue
transition ease-in-out duration-150`
} hover:text-foreground focus-visible:z-10 focus-ring`
return (
<div
className={`relative border ${borderOverride} rounded-md h-7`}
style={{ padding: 1, width: (width + 1) * 2 }}
role="group"
>
<span
style={{ width, translate: activeOption === options[1] ? '0px' : `${width - 2}px` }}
@@ -42,14 +42,20 @@ export const TwoOptionToggle = ({
/>
{options.map((option, index: number) => {
const isDisabled = disabledOptions.includes(option)
const isActive = activeOption === option
const optionButton = (
<span
<button
key={`toggle_${index}`}
type="button"
tabIndex={0}
aria-pressed={isActive}
// Prefer aria-disabled so TooltipTrigger asChild still receives hover/focus
aria-disabled={isDisabled || undefined}
style={{ width: width + 1 }}
className={cn(
activeOption === option ? 'text-foreground' : 'text-foreground-light',
isActive ? 'text-foreground' : 'text-foreground-light',
index === 0 ? 'right-0' : 'left-0',
buttonStyle(activeOption === option),
buttonStyle(isActive),
isDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
)}
onClick={() => {
@@ -59,13 +65,13 @@ export const TwoOptionToggle = ({
<span
className={cn(
'capitalize hover:text-foreground',
activeOption === option ? 'text-foreground' : 'text-foreground-light',
isActive ? 'text-foreground' : 'text-foreground-light',
isDisabled && 'hover:text-foreground-light'
)}
>
{option}
</span>
</span>
</button>
)
if (!isDisabled || !disabledOptionTooltip) return optionButton
@@ -314,7 +314,7 @@ export default function IntegrationsContent({
title="Grid view"
onClick={() => setFilters({ view: 'grid' })}
className={cn(
'relative flex items-center justify-center w-8 h-8 transition-colors rounded-l-lg focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'relative flex items-center justify-center w-8 h-8 rounded-l-lg focus-visible:z-10 focus-ring',
viewMode === 'grid'
? 'bg-surface-300 text-foreground'
: 'bg-surface-75 text-foreground-muted hover:text-foreground hover:bg-surface-200'
@@ -327,7 +327,7 @@ export default function IntegrationsContent({
title="List view"
onClick={() => setFilters({ view: 'list' })}
className={cn(
'relative flex items-center justify-center w-8 h-8 transition-colors border-l border-muted rounded-r-lg focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'relative flex items-center justify-center w-8 h-8 border-l border-muted rounded-r-lg focus-visible:z-10 focus-ring',
viewMode === 'list'
? 'bg-surface-300 text-foreground'
: 'bg-surface-75 text-foreground-muted hover:text-foreground hover:bg-surface-200'
@@ -346,7 +346,7 @@ export default function IntegrationsContent({
<Link
key={p.slug}
href={`/partners/catalog/${p.slug}`}
className="flex flex-col justify-start items-stretch group cursor-pointer transition rounded-xl focus-visible:ring-2 focus-visible:ring-foreground-lighter outline-hidden outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-foreground-lighter"
className="flex flex-col justify-start items-stretch group cursor-pointer rounded-xl focus-ring"
>
<Panel
hasActiveOnHover
@@ -20,13 +20,13 @@ const TwoOptionToggle = ({
) => `absolute top-0 z-1 text-xs inline-flex h-full items-center justify-center font-medium
${
isActive ? 'hover:text-foreground-light hover:text-foreground' : 'hover:text-foreground'
} hover:text-foreground focus:z-10 focus:outline-hidden focus:border-blue-300 focus:ring-blue
transition ease-in-out duration-150`
} hover:text-foreground focus-visible:z-10 focus-ring`
return (
<div
className={`relative border ${borderOverride} rounded-md h-7`}
style={{ padding: 1, width: (width + 1) * 2 }}
role="group"
>
<span
style={{ width, translate: activeOption === options[1] ? '0px' : `${width - 2}px` }}
@@ -36,28 +36,34 @@ const TwoOptionToggle = ({
'transition-all ease-in-out border border-strong'
)}
></span>
{options.map((option: any, index: number) => (
<span
key={`toggle_${index}`}
style={{ width: width + 1 }}
className={`
${activeOption === option ? 'text-foreground' : 'text-foreground-light'}
{options.map((option: any, index: number) => {
const isActive = activeOption === option
return (
<button
key={`toggle_${index}`}
type="button"
tabIndex={0}
aria-pressed={isActive}
style={{ width: width + 1 }}
className={`
${isActive ? 'text-foreground' : 'text-foreground-light'}
${index === 0 ? 'right-0' : 'left-0'}
${buttonStyle(activeOption === option)}
${buttonStyle(isActive)}
cursor-pointer
`}
onClick={() => onClickOption(option)}
>
<span
className={cn(
'capitalize hover:text-foreground',
activeOption === option ? 'text-foreground' : 'text-foreground-light'
)}
onClick={() => onClickOption(option)}
>
{option}
</span>
</span>
))}
<span
className={cn(
'capitalize hover:text-foreground',
isActive ? 'text-foreground' : 'text-foreground-light'
)}
>
{option}
</span>
</button>
)
})}
</div>
)
}
+1 -1
View File
@@ -48,7 +48,7 @@ const AnnouncementBadge = ({
hover:border-foreground-muted/30
shadow-md
overflow-hidden
focus-visible:outline-hidden focus-visible:ring-brand-600 focus-visible:ring-2 focus-visible:rounded-full
focus-ring rounded-full
`,
!badge && 'pl-5'
)}
+2 -2
View File
@@ -11,7 +11,7 @@ const Button = (props: Props) => {
const colorClass =
type === 'primary'
? 'px-3 py-2 shadow-xs border border-transparent text-white bg-brand-400 hover:bg-brand-300 focus:ring-2 focus:ring-offset-2 focus:ring-brand-300'
? 'px-3 py-2 shadow-xs border border-transparent text-white bg-brand-400 hover:bg-brand-300'
: 'text-brand-400 bg-none'
const textClass = type === 'primary' ? 'font-medium left-3 group-hover:left-0' : 'font-normal'
@@ -27,7 +27,7 @@ const Button = (props: Props) => {
const sharedClassName = `
group inline-flex items-center rounded-md text-sm
leading-4 transition focus:outline-hidden ${colorClass} ${className}
leading-4 focus-ring ${colorClass} ${className}
`
const content = (
@@ -38,8 +38,8 @@ export function LabelBadges({
href={changelogTagFilterUrl(label.name)}
className={
tiny
? 'inline-flex shrink-0 no-underline focus-visible:ring-brand-default rounded-sm focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden'
: 'inline-flex shrink-0 no-underline focus-visible:ring-brand-default rounded-md focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden'
? 'inline-flex shrink-0 no-underline focus-ring rounded-sm'
: 'inline-flex shrink-0 no-underline focus-ring rounded-md'
}
onClick={onBadgeClick}
>
+1 -1
View File
@@ -170,7 +170,7 @@ export function FeaturesMatrix({ features }: FeaturesMatrixProps) {
<TableCell className="px-3 py-3 min-w-0 hover:bg-transparent!">
<Link
href={`/features/${feature.slug}`}
className="flex items-center gap-2 text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter rounded min-w-0"
className="flex items-center gap-2 text-foreground focus-ring rounded min-w-0"
>
<div className="shrink-0 w-7 h-7 rounded-md bg-surface-200 border border-muted hidden md:flex items-center justify-center">
<feature.icon className="w-3.5 h-3.5 text-foreground-light group-hover/row:text-foreground transition-colors" />
+2 -2
View File
@@ -23,7 +23,7 @@ function ModulesNav({ activePage, docsUrl }: Props) {
key={currentModule.name}
className={cn(
'flex items-center gap-1.5 px-2 first:-ml-2 py-3 border-b border-transparent text-sm text-foreground-lighter hover:text-foreground',
'focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground focus-visible:outline-brand-600',
'focus-ring focus-visible:text-foreground',
currentModule.name === activePage && 'border-foreground-light text-foreground'
)}
href={currentModule.url ?? ''}
@@ -52,7 +52,7 @@ function ModulesNav({ activePage, docsUrl }: Props) {
<Link
className={cn(
'flex items-center gap-1.5 py-3 border-b border-transparent text-sm text-foreground-lighter hover:text-foreground',
'focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground focus-visible:outline-brand-600'
'focus-ring focus-visible:text-foreground'
)}
href={docsUrl}
>
@@ -27,7 +27,7 @@ export const DevelopersDropdown = () => {
<li key={link.text}>
<Link
href={link.url!}
className="flex group items-center gap-2 text-foreground-light text-sm hover:text-foreground focus-visible:text-foreground focus-visible:ring-2 focus-visible:outline-hidden focus-visible:rounded-sm focus-visible:ring-foreground-lighter"
className="flex group items-center gap-2 text-foreground-light text-sm hover:text-foreground focus-visible:text-foreground focus-ring rounded-sm"
>
{Icon && <Icon size={16} strokeWidth={1.3} />}
<span>{link.text}</span>
@@ -51,7 +51,7 @@ export const DevelopersDropdown = () => {
<div className="flex-col gap-2 py-8 px-10">
<Link
href="/blog"
className="group flex items-center gap-1 text-foreground-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-5 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-xs focus-visible:text-foreground"
className="group flex items-center gap-1 text-foreground-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-5 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-xs focus-visible:text-foreground"
>
<span>Blog</span>
<ChevronRight className="h-3 w-3 transition-transform will-change-transform -translate-x-1 group-hover:translate-x-0" />
@@ -61,7 +61,7 @@ export const DevelopersDropdown = () => {
<li key={post.title}>
<Link
href={post.url}
className="group flex flex-col focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm"
className="group flex flex-col focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-sm"
>
<p className="text-foreground-light mb-0 line-clamp-2 group-hover:text-foreground group-focus-visible:text-foreground">
{post.title}
+1 -1
View File
@@ -11,7 +11,7 @@ const HamburgerButton = (props: HamburgerButtonProps) => (
<button
tabIndex={0}
className={cn(
'text-foreground-lighter focus:ring-brand bg-transparent hover:text-foreground-light transition-colors hover:bg-overlay inline-flex items-center justify-center rounded-md p-2 focus:outline-hidden focus:ring-2 focus:ring-inset'
'text-foreground-lighter bg-transparent hover:text-foreground-light hover:bg-overlay inline-flex items-center justify-center rounded-md p-2 focus-ring'
)}
aria-expanded="false"
>
+1 -1
View File
@@ -31,7 +31,7 @@ const MenuItem = React.forwardRef<
href={href}
ref={ref}
className={cn(
'group/menu-item flex items-center text-foreground-light text-sm hover:text-foreground select-none gap-3 rounded-md p-2 leading-none no-underline outline-hidden focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground',
'group/menu-item flex items-center text-foreground-light text-sm hover:text-foreground select-none gap-3 rounded-md p-2 leading-none no-underline focus-ring focus-visible:text-foreground',
description && 'items-center',
className
)}
+4 -8
View File
@@ -90,8 +90,7 @@ export const MobileMenu = ({ open, setOpen, menu }: Props) => {
rounded-lg border
bg-alternative-200 text-foreground-light
hover:text-foreground hover:border-foreground-muted
focus-visible:text-foreground focus-visible:ring-2 focus-visible:outline-hidden
focus-visible:rounded-sm focus-visible:ring-foreground-lighter
focus-visible:text-foreground focus-ring
"
onClick={() => setOpen(false)}
>
@@ -194,10 +193,7 @@ export const MobileMenu = ({ open, setOpen, menu }: Props) => {
) : (
<Link
href={menuItem.url ?? '/'}
className={cn(
className,
'block focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:rounded-sm'
)}
className={cn(className, 'block focus-ring rounded-sm')}
onClick={() => setOpen(false)}
>
{menuItem.title}
@@ -224,7 +220,7 @@ export const MobileMenu = ({ open, setOpen, menu }: Props) => {
<Link
href="/"
as="/"
className="block w-auto h-6 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-xs"
className="block w-auto h-6 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-xs"
>
<SupabaseWordmark />
</Link>
@@ -232,7 +228,7 @@ export const MobileMenu = ({ open, setOpen, menu }: Props) => {
tabIndex={0}
onClick={() => setOpen(false)}
type="button"
className="inline-flex items-center justify-center p-2 rounded-md text-foreground-lighter focus:ring-brand hover:text-foreground-light transition-colors focus:outline-hidden focus:ring-2 focus:ring-inset"
className="inline-flex items-center justify-center p-2 rounded-md text-foreground-lighter hover:text-foreground-light focus-ring"
>
<span className="sr-only">Close menu</span>
<svg
+6 -6
View File
@@ -51,8 +51,8 @@ export const ProductDropdown = () => {
flex items-start gap-2
text-xs leading-none
text-foreground-light hover:text-foreground
no-underline outline-hidden select-none
focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground
no-underline select-none
focus-ring focus-visible:text-foreground
"
>
<div className="w-8 h-8 min-w-8 shrink-0 bg-background border flex items-center justify-center rounded-md">
@@ -98,8 +98,8 @@ export const ProductDropdown = () => {
flex items-start gap-2
text-xs leading-none
text-foreground-light hover:text-foreground
no-underline outline-hidden select-none
focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground
no-underline select-none
focus-ring focus-visible:text-foreground
"
>
<div className="w-8 h-8 min-w-8 shrink-0 bg-background border flex items-center justify-center rounded-md">
@@ -127,7 +127,7 @@ export const ProductDropdown = () => {
<div>
<Link
href="/customers"
className="group flex items-center gap-1 text-foreground-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-4 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-xs focus-visible:text-foreground"
className="group flex items-center gap-1 text-foreground-lighter hover:text-foreground text-xs uppercase tracking-widest font-mono mb-4 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-xs focus-visible:text-foreground"
>
Customer Stories
<ChevronRight className="h-3 w-3 transition-transform will-change-transform -translate-x-1 group-hover:translate-x-0" />
@@ -137,7 +137,7 @@ export const ProductDropdown = () => {
<li key={customer.organization}>
<Link
href={customer.url}
className="group flex items-center gap-3 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-sm"
className="group flex items-center gap-3 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-sm"
>
<div className="relative rounded-md bg-background border group-hover:border-foreground-muted/50 h-14 w-28 xl:h-14 xl:w-20 shrink-0 overflow-auto">
<Image
@@ -93,7 +93,7 @@ const RightClickBrandLogo = () => {
onContextMenu={handleRightClick}
onFocus={handleKeyboardOpen}
onKeyDown={(e) => e.key === 'Enter' && router?.push('/')}
className="block w-auto h-6 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative focus-visible:rounded-xs"
className="block w-auto h-6 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-xs"
>
<SupabaseWordmark />
</Link>
@@ -1,11 +1,10 @@
import { motion, AnimatePresence } from 'framer-motion'
import { navData as DevelopersData } from 'data/Solutions'
import { AnimatePresence, motion } from 'framer-motion'
import { ArrowLeftRight, ChevronRight } from 'lucide-react'
import Link from 'next/link'
import { useState } from 'react'
import { useWindowSize } from 'react-use'
import { navData as DevelopersData } from 'data/Solutions'
type LinkProps = {
text: string
description?: string
@@ -50,7 +49,7 @@ const LinksGroup = ({ links, label }: { links: LinkProps[]; label: string }) =>
<li key={link.text}>
<Link
href={link.url!}
className="flex group items-center gap-2 text-foreground-light text-sm hover:text-foreground focus-visible:text-foreground focus-visible:ring-2 focus-visible:outline-hidden focus-visible:rounded-sm focus-visible:ring-foreground-lighter"
className="flex group items-center gap-2 text-foreground-light text-sm hover:text-foreground focus-visible:text-foreground focus-ring rounded-sm"
>
{Icon && <Icon size={16} strokeWidth={1.3} />}
<span>{link.text}</span>
+1 -1
View File
@@ -130,7 +130,7 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => {
<NavigationMenuTrigger
className={cn(
buttonVariants({ variant: 'text', size: 'small' }),
'bg-transparent! hover:text-brand-link data-open:text-brand-link! data-radix-collection-item:focus-visible:ring-2 data-radix-collection-item:focus-visible:ring-foreground-lighter data-radix-collection-item:focus-visible:text-foreground px-2 h-auto'
'bg-transparent! hover:text-brand-link data-open:text-brand-link! focus-ring focus-visible:text-foreground px-2 h-auto'
)}
>
{menuItem.title}
@@ -189,15 +189,18 @@ const PricingComputeSection = () => {
<button
tabIndex={0}
onClick={() => setShowTable(!showTable)}
className="w-full p-2 border-t border-muted text-foreground focus-visible:outline-brand-600 focus-visible:rounded-b-xl text-sm bg-alternative flex items-center justify-center gap-2"
className="group w-full p-2 border-t border-muted text-foreground outline-hidden rounded-b-xl text-sm bg-alternative flex items-center justify-center"
>
<ChevronDownIcon
className={cn(
'w-4 transition-transform transform origin-center',
showTable ? 'rotate-180' : 'rotate-0'
)}
/>{' '}
{!showTable ? 'Expand' : 'Hide'} Pricing breakdown
{/* Label-scoped ring: full-bleed focus-ring clips under Panel overflow-hidden */}
<span className="inline-flex items-center gap-2 rounded-md px-2 py-0.5 group-focus-visible:ring-2 group-focus-visible:ring-ring">
<ChevronDownIcon
className={cn(
'w-4 transition-transform transform origin-center',
showTable ? 'rotate-180' : 'rotate-0'
)}
/>
{!showTable ? 'Expand' : 'Hide'} Pricing breakdown
</span>
</button>
</div>
</Panel>
+1 -1
View File
@@ -30,7 +30,7 @@ const ProductCard = ({
<Link
href={url}
className={cn(
'group relative w-full sm:h-[400px] flex flex-col gap-5 lg:flex-row focus:outline-hidden focus:border-none focus:ring-brand-600 focus:ring-2 focus:rounded-xl',
'group relative w-full sm:h-[400px] flex flex-col gap-5 lg:flex-row focus-ring rounded-xl',
className
)}
onClick={onClick}
+1 -1
View File
@@ -21,7 +21,7 @@ function ProductsNav({ activePage }: Props) {
key={product.name}
className={cn(
'flex items-center gap-1.5 px-2 first:-ml-2 py-4 border-b border-transparent text-sm text-foreground-lighter hover:text-foreground',
'focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground focus-visible:outline-brand-600',
'focus-ring focus-visible:text-foreground',
product.name === activePage && 'border-foreground-light text-foreground'
)}
href={`/${isAuth ? 'auth' : product.name.toLowerCase().replace(' ', '-')}`}
+1 -1
View File
@@ -88,7 +88,7 @@ function SolutionsStickyNav({ type, activeItem, className }: Props) {
key={item.name}
className={cn(
'flex items-center gap-1.5 px-2 first:-ml-2 py-4 border-b border-transparent text-sm text-foreground-lighter hover:text-foreground',
'focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:text-foreground focus-visible:outline-brand-600',
'focus-ring focus-visible:text-foreground',
isActive && 'border-foreground-light text-foreground'
)}
href={item.href}
+1 -1
View File
@@ -384,7 +384,7 @@ function ChangelogIndex({ featured, restIndex, allIndex }: PageProps) {
<a
key={`${entry.number}-${label.name}`}
href={changelogTagFilterUrl(label.name)}
className="group inline-flex no-underline focus-visible:ring-brand-default rounded-md focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden"
className="group inline-flex no-underline focus-ring rounded-md"
>
<Badge className="group-hover:text-foreground-light text-foreground-lighter group-hover:border-foreground-muted px-1.5 py-px text-[11px] tracking-normal lowercase">
{changelogLabelDisplayName(label.name)}
+2 -2
View File
@@ -220,7 +220,7 @@ const Press = () => {
href={x.href}
key={x.href}
target="_blank"
className="flex flex-col justify-start items-stretch group cursor-pointer transition rounded-xl focus-visible:ring-2 focus-visible:ring-foreground-lighter outline-hidden outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-foreground-lighter"
className="flex flex-col justify-start items-stretch group cursor-pointer rounded-xl focus-ring"
>
<Panel
hasActiveOnHover
@@ -247,7 +247,7 @@ const Press = () => {
href={x.href}
key={x.href}
target="_blank"
className="flex flex-col justify-start items-stretch group cursor-pointer transition rounded-xl focus-visible:ring-2 focus-visible:ring-foreground-lighter outline-hidden outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-foreground-lighter"
className="flex flex-col justify-start items-stretch group cursor-pointer rounded-xl focus-ring"
>
<Panel
hasActiveOnHover
+3 -3
View File
@@ -280,7 +280,7 @@ function FeaturesPage() {
title="Grid view"
onClick={() => setViewMode('grid')}
className={cn(
'relative flex items-center justify-center w-8 h-8 transition-colors rounded-l-lg focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'relative flex items-center justify-center w-8 h-8 rounded-l-lg focus-visible:z-10 focus-ring',
viewMode === 'grid'
? 'bg-surface-300 text-foreground'
: 'bg-surface-75 text-foreground-muted hover:text-foreground hover:bg-surface-200'
@@ -293,7 +293,7 @@ function FeaturesPage() {
title="Matrix view"
onClick={() => setViewMode('matrix')}
className={cn(
'relative flex items-center justify-center w-8 h-8 transition-colors border-l border-muted rounded-r-lg focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground-lighter focus-visible:ring-offset-1 focus-visible:ring-offset-background',
'relative flex items-center justify-center w-8 h-8 border-l border-muted rounded-r-lg focus-visible:z-10 focus-ring',
viewMode === 'matrix'
? 'bg-surface-300 text-foreground'
: 'bg-surface-75 text-foreground-muted hover:text-foreground hover:bg-surface-200'
@@ -316,7 +316,7 @@ function FeaturesPage() {
<Link
key={`feat-${feature.title}`}
href={`/features/${feature.slug}`}
className="flex flex-col justify-start items-stretch group cursor-pointer transition rounded-xl focus-visible:ring-2 focus-visible:ring-foreground-lighter outline-hidden outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-foreground-lighter"
className="flex flex-col justify-start items-stretch group cursor-pointer rounded-xl focus-ring"
>
<Panel
hasActiveOnHover
+1 -1
View File
@@ -29,7 +29,7 @@ export function TextLink({
<Link
href={url}
className={cn(
'group/text-link text-foreground-light hover:text-foreground mt-3 block cursor-pointer text-sm focus-visible:ring-2 focus-visible:outline-hidden focus-visible:rounded-xs focus-visible:ring-foreground-lighter focus-visible:text-foreground',
'group/text-link text-foreground-light hover:text-foreground mt-3 block cursor-pointer text-sm focus-ring focus-visible:rounded-xs focus-visible:text-foreground',
className
)}
target={target}
@@ -293,8 +293,8 @@ const MultiSelectorTrigger = React.forwardRef<HTMLButtonElement, MultiSelectorTr
className={cn(
'flex w-full min-w-[200px] min-h-[40px] items-center justify-between rounded-md border',
'border-alternative bg-control px-3 py-2 text-sm',
'ring-offset-background placeholder:text-muted-foreground',
'focus:outline-hidden focus:ring-2 focus:ring-background-control focus:ring-offset-2 focus-visible:ring-offset-foreground-muted',
'placeholder:text-muted-foreground',
'focus-ring',
'disabled:cursor-not-allowed disabled:opacity-50',
'hover:border-primary transition-colors duration-200',
className
@@ -48,14 +48,14 @@ const RadioGroupStackedItem = React.forwardRef<
// Enabled/hover states
'enabled:cursor-pointer enabled:hover:bg-surface-300 enabled:hover:border-foreground-muted',
// Focus state
'focus:ring-background-control focus:border-control focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted',
'focus-ring enabled:focus-visible:border-control',
// Z-index for interactions
'hover:z-1 focus-visible:z-1 data-[state=checked]:z-1',
// Checked state
'data-[state=checked]:ring-1 data-[state=checked]:ring-border',
'data-[state=checked]:bg-surface-300 data-[state=checked]:border-foreground-muted',
// Transitions and group
'transition group',
// Colors only — avoid bare `transition` so the focus ring does not animate
'transition-colors group',
props.className
)}
>
@@ -66,12 +66,11 @@ const RadioGroupStackedItem = React.forwardRef<
// Base styles
'aspect-square h-4 w-4 min-w-4 min-h-4 rounded-full border relative',
'flex items-center justify-center',
'ring-offset-background transition',
// States
'group-data-[state=checked]:border-foreground-muted',
'group-focus:border-foreground-muted group-focus:outline-hidden',
'group-focus-visible:ring-2 group-focus-visible:ring-background-control group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-foreground-muted',
'group-hover:border-foreground-muted'
'group-focus-visible:border-foreground-muted',
'group-focus-visible:ring-2 group-focus-visible:ring-ring group-focus-visible:ring-offset-2 group-focus-visible:ring-offset-background',
'group-hover:border-foreground-muted transition-colors'
)}
>
<RadioGroupPrimitive.Indicator className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
@@ -114,7 +114,7 @@ const DialogContent = React.forwardRef<
{!hideClose && (
<DialogPrimitive.Close
className={cn(
'absolute p-0.5 right-3.5 top-3.5 rounded-xs opacity-20 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'absolute p-0.5 right-3.5 top-3.5 rounded-xs opacity-20 transition-opacity hover:opacity-100 focus-ring disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'hit-area-6'
)}
>
@@ -197,7 +197,7 @@ const DialogClose = React.forwardRef<
<DialogPrimitive.Close
ref={ref}
className={cn(
'opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
'opacity-70 transition-opacity hover:opacity-100 focus-ring disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-foreground-muted',
className
)}
{...props}
@@ -180,7 +180,7 @@ const SheetContent = React.forwardRef<
{showClose ? (
<SheetPrimitive.Close
className={cn(
'absolute right-4 top-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary',
'absolute right-4 top-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-ring disabled:pointer-events-none data-[state=open]:bg-secondary',
'hit-area-6'
)}
>
@@ -54,10 +54,10 @@ const SonnerToaster = ({ toastOptions, ...props }: ToasterProps) => {
'group toast group-[.toaster]:!bg-destructive-200 group-[.toaster]:!border-destructive-500',
closeButton: cn(
// unset all styles set from sonner
'absolute right-2 top-2 size-6 flex items-center justify-center rounded-md text-foreground-light opacity-0 transition',
'hover:text-foreground hover:bg-surface-200 focus:opacity-100 focus:outline-hidden focus:ring-2 group-hover:opacity-100',
'absolute right-2 top-2 size-6 flex items-center justify-center rounded-md text-foreground-light opacity-0 transition-opacity',
'hover:text-foreground hover:bg-surface-200 focus-visible:opacity-100 focus-ring group-hover:opacity-100',
'group-[.destructive]:text-destructive-300 group-[.destructive]:hover:text-destructive-50',
'group-[.destructive]:focus:ring-destructive-400 group-[.destructive]:focus:ring-offset-destructive-600',
'group-[.destructive]:focus-visible:ring-destructive-400 group-[.destructive]:focus-visible:ring-offset-destructive-600',
'left-auto transform-none border-0 border-transparent'
),
content: 'grow',