Skip to content

Commit f662237

Browse files
authoredAug 31, 2024··
[v3] avoid focus-visible style being cut off by overflow-hidden (#3160)
aa
1 parent db3b48d commit f662237

File tree

7 files changed

+32
-23
lines changed

7 files changed

+32
-23
lines changed
 

‎.changeset/short-icons-impress.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'nextra-theme-docs': patch
3+
'nextra': patch
4+
---
5+
6+
avoid focus-visible style being cut off by overflow-hidden

‎examples/swr-site/pages/en/index.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Callout, Tabs } from 'nextra/components'
99
{/* wrapped with {} to mark it as javascript so mdx will not put it under a p tag */}
1010
{<h1 className="text-center font-extrabold md:text-5xl mt-8">SWR</h1>}
1111

12-
<Tabs items={['JavaScript', 'C++', {label:'C', disabled: true}, 'Python']} defaultIndex={1}>
12+
<Tabs items={['JavaScript', 'C++', {label:'C', disabled: true}, 'Python', 'TypeScript', 'GraphQL', 'Rust', 'C#', 'Go', 'HTML', 'CSS', 'plaintext', 'bash']} defaultIndex={1}>
1313
<Tabs.Tab>
1414
```js filename="hi.js"
1515
import { useState, useEffect } from 'react';

‎packages/nextra-theme-docs/src/components/collapse.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function Collapse({
3838
} else {
3939
container.style.height = `${inner.clientHeight}px`
4040
}
41-
41+
container.style.overflow = isOpen ? '' : 'hidden'
4242
if (isOpen) {
4343
animationRef.current = window.setTimeout(() => {
4444
// should be style property in kebab-case, not css class name
@@ -62,7 +62,7 @@ export function Collapse({
6262
return (
6363
<div
6464
ref={containerRef}
65-
className="_transform-gpu _overflow-hidden _transition-all _ease-in-out motion-reduce:_transition-none"
65+
className="_transform-gpu _transition-all _ease-in-out motion-reduce:_transition-none"
6666
style={initialOpen.current || horizontal ? undefined : { height: 0 }}
6767
>
6868
<div

‎packages/nextra-theme-docs/src/components/sidebar.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ export function Sidebar({
407407
>
408408
<div
409409
className={cn(
410-
'_overflow-y-auto _overflow-x-hidden',
410+
'_overflow-y-auto',
411411
'_p-4 _grow md:_h-[calc(100vh-var(--nextra-navbar-height)-var(--nextra-menu-height))]',
412412
showSidebar ? 'nextra-scrollbar' : 'no-scrollbar'
413413
)}
@@ -444,7 +444,8 @@ export function Sidebar({
444444
<div
445445
className={cn(
446446
'nextra-sidebar-footer _sticky _bottom-0',
447-
'_flex _items-center _gap-2 _mx-4 _py-4',
447+
'_flex _items-center _gap-2 _py-4',
448+
'_mx-3 _px-1', // to hide focused sidebar links
448449
showSidebar
449450
? hasI18n && '_justify-end'
450451
: '_py-4 _flex-wrap _justify-center'

‎packages/nextra-theme-docs/src/components/toc.tsx

+10-9
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ export function TOC({ toc, filePath }: TOCProps): ReactElement {
4949
block: 'center',
5050
inline: 'center',
5151
scrollMode: 'always',
52-
boundary: tocRef.current
52+
boundary: tocRef.current!.parentElement
5353
})
5454
}
5555
}, [activeSlug])
5656

5757
return (
5858
<div
5959
className={cn(
60-
'nextra-scrollbar _sticky _top-16 _overflow-y-auto _pr-4 _pt-6 _text-sm [hyphens:auto]',
60+
'nextra-scrollbar _sticky _top-16 _overflow-y-auto _px-4 _pt-6 _text-sm [hyphens:auto]',
6161
'_max-h-[calc(100vh-var(--nextra-navbar-height)-env(safe-area-inset-bottom))] ltr:_-mr-4 rtl:_-ml-4'
6262
)}
6363
>
@@ -74,16 +74,16 @@ export function TOC({ toc, filePath }: TOCProps): ReactElement {
7474
className={cn(
7575
{
7676
2: '_font-semibold',
77-
3: 'ltr:_pl-4 rtl:_pr-4',
78-
4: 'ltr:_pl-8 rtl:_pr-8',
79-
5: 'ltr:_pl-12 rtl:_pr-12',
80-
6: 'ltr:_pl-16 rtl:_pr-16'
77+
3: 'ltr:_ml-4 rtl:_mr-4',
78+
4: 'ltr:_ml-8 rtl:_mr-8',
79+
5: 'ltr:_ml-12 rtl:_mr-12',
80+
6: 'ltr:_ml-16 rtl:_mr-16'
8181
}[depth],
82-
'_inline-block _transition-colors _subpixel-antialiased',
82+
'_block _transition-colors _subpixel-antialiased',
8383
activeAnchor[id]?.isActive
8484
? '_text-primary-600 contrast-more:!_text-primary-600'
8585
: '_text-gray-500 hover:_text-gray-900 dark:_text-gray-400 dark:hover:_text-gray-300',
86-
'contrast-more:_text-gray-900 contrast-more:_underline contrast-more:dark:_text-gray-50 _w-full _break-words'
86+
'contrast-more:_text-gray-900 contrast-more:_underline contrast-more:dark:_text-gray-50 _break-words'
8787
)}
8888
>
8989
{removeLinks(value)}
@@ -98,7 +98,8 @@ export function TOC({ toc, filePath }: TOCProps): ReactElement {
9898
<div
9999
className={cn(
100100
hasHeadings && 'nextra-toc-footer _mt-8 _pt-8',
101-
'_sticky _bottom-0 _flex _flex-col _items-start _gap-2 _pb-8'
101+
'_sticky _bottom-0 _flex _flex-col _items-start _gap-2 _pb-8',
102+
'_-mx-1 _px-1' // to hide focused toc links
102103
)}
103104
>
104105
{themeConfig.feedback.content ? (

‎packages/nextra-theme-docs/src/mdx-components.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,7 @@ const DEFAULT_COMPONENTS: MDXComponents = {
369369
<nav className={classes.toc} aria-label="table of contents" />
370370
)
371371
) : (
372-
<nav
373-
className={cn(classes.toc, '_px-4')}
374-
aria-label="table of contents"
375-
>
372+
<nav className={classes.toc} aria-label="table of contents">
376373
{renderComponent(themeConfig.toc.component, {
377374
toc: themeConfig.toc.float ? toc : [],
378375
filePath: config.filePath

‎packages/nextra/src/client/components/tabs.tsx

+9-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import cn from 'clsx'
33
import type { ComponentProps, ReactElement, ReactNode } from 'react'
44
import { useCallback, useEffect, useState } from 'react'
55

6-
type TabItem = string | ReactNode
6+
type TabItem = string | ReactElement
77

88
type TabObjectItem = {
99
label: TabItem
@@ -80,8 +80,11 @@ function Tabs_({
8080
defaultIndex={defaultIndex}
8181
onChange={handleChange}
8282
>
83-
<div className="nextra-scrollbar _overflow-x-auto _overflow-y-hidden _overscroll-x-contain">
84-
<HeadlessTab.List className="_mt-4 _flex _w-max _min-w-full _border-b _border-gray-200 _pb-px dark:_border-neutral-800">
83+
<div
84+
className={cn('nextra-scrollbar _overflow-x-auto')}
85+
tabIndex={-1} // disables focus in Firefox
86+
>
87+
<HeadlessTab.List className="_mt-4 _flex _w-max _min-w-full _gap-2 _border-b _border-gray-200 _pb-px dark:_border-neutral-800">
8588
{items.map((item, index) => {
8689
const disabled = isTabObjectItem(item) && item.disabled
8790
return (
@@ -90,7 +93,8 @@ function Tabs_({
9093
disabled={disabled}
9194
className={({ selected }) =>
9295
cn(
93-
'_mr-2 _rounded-t _p-2 _font-medium _leading-5 _transition-colors',
96+
'_ring-inset',
97+
'_rounded-t _p-2 _font-medium _leading-5 _transition-colors',
9498
'_-mb-0.5 _select-none _border-b-2',
9599
selected
96100
? '_border-primary-500 _text-primary-600'
@@ -118,7 +122,7 @@ function Tab({
118122
...props
119123
}: Omit<ComponentProps<typeof HeadlessTab.Panel>, 'static'>): ReactElement {
120124
return (
121-
<HeadlessTab.Panel {...props} unmount={unmount} className="_rounded _pt-6">
125+
<HeadlessTab.Panel {...props} unmount={unmount} className="_rounded _mt-6">
122126
{children}
123127
</HeadlessTab.Panel>
124128
)

0 commit comments

Comments
 (0)
Please sign in to comment.