From cc91f90d2dbfc46d88f1530bc9f1c66da70b4245 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:03 -0500 Subject: [PATCH 1/6] fix: Improve mobile responsive styling across components - NavBar: Responsive logo size, margins, spacing; hide AppModeDropdown on mobile - LandingTabs: Show logo on mobile with responsive sizing - Footer: Responsive column width and logo wrapping - ClassicForm: Stack collection checkboxes vertically on mobile - PaperForm: Stack journal search grid vertically on mobile - Pager: Responsive panel width, hide arrows on mobile - ListActions: Responsive margin for button stack - ResultList Item: Responsive index column width - HomePage: Responsive stats and video section widths - Theme: Add overflowX hidden to prevent horizontal scroll --- src/components/ClassicForm/ClassicForm.tsx | 17 ++++++++------- src/components/Footer/Footer.tsx | 8 +++---- src/components/LandingTabs/LandingTabs.tsx | 25 +++++++++++++--------- src/components/NavBar/NavBar.tsx | 20 +++++++++++------ src/components/Pager/Pager.tsx | 14 ++++++++---- src/components/ResultList/Item/Item.tsx | 2 +- src/components/ResultList/ListActions.tsx | 2 +- src/pages/index.tsx | 12 ++++++++--- src/pages/paper-form.tsx | 15 ++++++------- src/theme.ts | 1 + 10 files changed, 70 insertions(+), 46 deletions(-) diff --git a/src/components/ClassicForm/ClassicForm.tsx b/src/components/ClassicForm/ClassicForm.tsx index c918d0020..435ba9737 100644 --- a/src/components/ClassicForm/ClassicForm.tsx +++ b/src/components/ClassicForm/ClassicForm.tsx @@ -11,7 +11,6 @@ import { FormControl, FormHelperText, FormLabel, - HStack, Input, InputGroup, InputLeftElement, @@ -102,7 +101,7 @@ export const ClassicForm = (props: IClassicFormProps) => { Collection - + Astronomy @@ -115,7 +114,7 @@ export const ClassicForm = (props: IClassicFormProps) => { Earth Science - + @@ -224,14 +223,14 @@ export const ClassicForm = (props: IClassicFormProps) => { Property - + Refereed only Articles only - + @@ -338,10 +337,12 @@ const CurrentQuery = (props: { control: Control }) => { title="Generated Query" defaultOpen description={ - - {query} + + + {query} + {typeof query === 'string' ? : null} - + } > ); diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 8fc9a68e2..d4ef48bd9 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, HStack, Text, VisuallyHidden } from '@chakra-ui/react'; +import { Box, Flex, Text, VisuallyHidden } from '@chakra-ui/react'; import { FC } from 'react'; import { EXTERNAL_URLS } from '@/config'; @@ -18,7 +18,7 @@ export const Footer: FC = () => { color="gray.50" sx={{ a: { color: 'gray.50' } }} > - + © The{' '} @@ -43,7 +43,7 @@ export const Footer: FC = () => { ) : null} - + Smithsonian Institution @@ -58,7 +58,7 @@ export const Footer: FC = () => { - + *The material contained in this document is based upon work supported by a National Aeronautics and Space diff --git a/src/components/LandingTabs/LandingTabs.tsx b/src/components/LandingTabs/LandingTabs.tsx index 5a43cf92d..7beb39cbf 100644 --- a/src/components/LandingTabs/LandingTabs.tsx +++ b/src/components/LandingTabs/LandingTabs.tsx @@ -1,4 +1,4 @@ -import { Box, Center, Flex, HStack, Icon, Show, VisuallyHidden, Text, DarkMode } from '@chakra-ui/react'; +import { Box, Center, Flex, HStack, Icon, VisuallyHidden, Text, DarkMode } from '@chakra-ui/react'; import { useStore } from '@/store'; import { AppMode, ByADSModes } from '@/types'; import Image from 'next/image'; @@ -91,15 +91,20 @@ const TitleLogo = () => { return (
- - - {ByADSModes.includes(mode) ? ( - <> - by ADS - ads logo - - ) : null} - + + {ByADSModes.includes(mode) ? ( + <> + + by ADS + + ads logo + + ) : null} Science Explorer
); diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx index 491272926..ed06432ce 100644 --- a/src/components/NavBar/NavBar.tsx +++ b/src/components/NavBar/NavBar.tsx @@ -32,14 +32,20 @@ export const NavBar: FC = () => { return ( - - - + + + - + {ByADSModes.includes(mode) ? ( by ADS @@ -50,10 +56,12 @@ export const NavBar: FC = () => { - + + + - + diff --git a/src/components/Pager/Pager.tsx b/src/components/Pager/Pager.tsx index b46a64786..004ed4f7e 100644 --- a/src/components/Pager/Pager.tsx +++ b/src/components/Pager/Pager.tsx @@ -97,14 +97,20 @@ export const Pager = (props: IPagerProps) => { onBlur={() => setShouldWiggle(false)} > - + - + {title} {pages.map((page, index) => ( - + {typeof page.content === 'function' ? page.content({ page: index, title: page.title, next, prev }) : page.content} @@ -112,7 +118,7 @@ export const Pager = (props: IPagerProps) => { ))} - + { mr="2" px="2" borderLeftRadius="md" - w="64px" + w={{ base: '40px', md: '64px' }} > { )} - + }> Bulk Actions diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 8d8c45047..303c46052 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -337,12 +337,12 @@ const Stats = () => { @@ -380,7 +380,13 @@ const Stats = () => { const IntroVideoSection = () => { return ( - + {
{/* Bibstem picker */} - - + + {isClient ? ( { )} - + Year {errors.year && errors.year.message} - + Volume {errors.volume && errors.volume.message} - + Page / Id @@ -446,10 +446,7 @@ const journalQueryNotEmpty = pipe< any((v) => v.length > 0), ); -export const getSearchQuery = async ( - formParams: PaperFormState[PaperFormType], - queryClient: QueryClient, -) => { +export const getSearchQuery = async (formParams: PaperFormState[PaperFormType], queryClient: QueryClient) => { switch (formParams.form) { case PaperFormType.JOURNAL_QUERY: { if (journalQueryNotEmpty(formParams)) { diff --git a/src/theme.ts b/src/theme.ts index 252a8698e..07ca187af 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -73,6 +73,7 @@ export const theme = extendTheme( fontSize: 'md', fontWeight: 'normal', backgroundColor: mode('white', 'gray.800')(props), + overflowX: 'hidden', }, a: { color: mode('blue.500', 'blue.200')(props), From 4db5611d0117345266eb4045a51d4be95b05acb5 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:03 -0500 Subject: [PATCH 2/6] fix: Improve mobile UX for search results and abstract pages Search results page: - Result cards stack title/actions vertically on mobile - Increased touch targets (44px min) for action buttons - Added tooltip delay to prevent accidental triggering on touch - Metadata row wraps properly on narrow screens Abstract page: - Details table stacks label/value vertically on mobile - Keywords and tags wrap instead of overflowing - Sub-page navigation menu centered on mobile - Added overflow protection to details container --- .../AbstractDetails/AbstractDetails.tsx | 95 +++++++++++++++---- .../AbstractSideNav/AbstractSideNav.tsx | 5 +- src/components/ResultList/Item/Item.tsx | 18 +++- .../ResultList/Item/ItemResourceDropdowns.tsx | 26 +++-- src/components/ResultList/ListActions.tsx | 11 ++- src/utils/logging.ts | 19 ++-- 6 files changed, 128 insertions(+), 46 deletions(-) diff --git a/src/components/AbstractDetails/AbstractDetails.tsx b/src/components/AbstractDetails/AbstractDetails.tsx index 9732e30fd..82ce086b4 100644 --- a/src/components/AbstractDetails/AbstractDetails.tsx +++ b/src/components/AbstractDetails/AbstractDetails.tsx @@ -63,12 +63,25 @@ export const AbstractDetails = ({ doc }: IDetailsProps): ReactElement => { const { isOpen: isCitationOpen, onOpen: onCitationOpen, onClose: onCitationClose } = useDisclosure(); return ( - + Details - - +
+ {(pub_raw) => ( <> @@ -127,9 +140,29 @@ const Detail = (props: IDetailProps): ReactEleme const normalizedValue: string = Array.isArray(value) ? value.join('; ') : value; return ( - - - + +
{label} +
+ {label} + {href && ( {normalizedValue} @@ -184,9 +217,17 @@ const Keywords = memo(({ keywords }: { keywords: Array }) => { {(keywords) => ( {keywords.map((keyword) => ( - - - {keyword} + + + {keyword} ; ids: Arr {(keywords) => ( {keywords.map((keyword, index) => ( - - + + {shortenKeyword(keyword)} @@ -267,9 +316,9 @@ const Collections = memo(({ collections }: { collections: Array }) => { {(collections) => ( {collections.map((collection) => ( - - - {collection} + + + {collection} }) => { {(bibgroups) => ( {bibgroups.map((bibgroup) => ( - - - {bibgroup} + + + {bibgroup} ; id {(features) => ( {features.map((feature, index) => ( - - + + { menuItems={menuItems} activeItem={activeItem} display={{ base: 'initial', lg: 'none' }} - mx={2} + mx="auto" + maxW="400px" + w="full" + px={2} /> ); diff --git a/src/components/ResultList/Item/Item.tsx b/src/components/ResultList/Item/Item.tsx index 338dd4e1d..5d5476202 100644 --- a/src/components/ResultList/Item/Item.tsx +++ b/src/components/ResultList/Item/Item.tsx @@ -141,17 +141,23 @@ export const Item = (props: IItemProps): ReactElement => { {hideCheckbox ? null : } - - + + - + {!isClient || hideActions ? null : } @@ -159,11 +165,13 @@ export const Item = (props: IItemProps): ReactElement => { {author_count > 0 && ( )} - + {formattedPubDate} {formattedPubDate && pub ? · : ''} - {truncatedPub} + + {truncatedPub} + {cite && (formattedPubDate || pub) ? · : null} {cite} diff --git a/src/components/ResultList/Item/ItemResourceDropdowns.tsx b/src/components/ResultList/Item/ItemResourceDropdowns.tsx index 783aa204f..b60c4aa0f 100644 --- a/src/components/ResultList/Item/ItemResourceDropdowns.tsx +++ b/src/components/ResultList/Item/ItemResourceDropdowns.tsx @@ -144,11 +144,11 @@ export const ItemResourceDropdowns = ({ doc, defaultCitation }: IItemResourceDro }; return ( - + {/* orcid menu */} {/* full resources menu */} - + } isDisabled={fullSourceItems.length === 0} variant="link" - size="sm" + size={{ base: 'md', md: 'sm' }} + minW={{ base: '44px', md: 'auto' }} + minH={{ base: '44px', md: 'auto' }} /> {fullSourceItems.length > 0 && ( @@ -171,7 +173,7 @@ export const ItemResourceDropdowns = ({ doc, defaultCitation }: IItemResourceDro {/* reference and citation items menu */} - + } isDisabled={referenceItems.length === 0} variant="link" - size="sm" + size={{ base: 'md', md: 'sm' }} + minW={{ base: '44px', md: 'auto' }} + minH={{ base: '44px', md: 'auto' }} /> {referenceItems.length > 0 && ( @@ -194,7 +198,7 @@ export const ItemResourceDropdowns = ({ doc, defaultCitation }: IItemResourceDro {/* data product items menu */} - + } isDisabled={dataProductItems.length === 0} variant="link" - size="sm" + size={{ base: 'md', md: 'sm' }} + minW={{ base: '44px', md: 'auto' }} + minH={{ base: '44px', md: 'auto' }} /> {dataProductItems.length > 0 && ( @@ -216,14 +222,16 @@ export const ItemResourceDropdowns = ({ doc, defaultCitation }: IItemResourceDro {/* share menu */} - + } variant="link" - size="sm" + size={{ base: 'md', md: 'sm' }} + minW={{ base: '44px', md: 'auto' }} + minH={{ base: '44px', md: 'auto' }} /> Copy URL diff --git a/src/components/ResultList/ListActions.tsx b/src/components/ResultList/ListActions.tsx index 38bc9145d..8d32b48a8 100644 --- a/src/components/ResultList/ListActions.tsx +++ b/src/components/ResultList/ListActions.tsx @@ -218,9 +218,9 @@ export const ListActions = (props: IListActionsProps): ReactElement => { )} - + - }> + } size={{ base: 'md', md: 'md' }}> Bulk Actions @@ -250,7 +250,12 @@ export const ListActions = (props: IListActionsProps): ReactElement => { - } data-testid="explorer-menu-btn"> + } + data-testid="explorer-menu-btn" + size={{ base: 'md', md: 'md' }} + > Explore diff --git a/src/utils/logging.ts b/src/utils/logging.ts index d0d9248ed..fc1307162 100644 --- a/src/utils/logging.ts +++ b/src/utils/logging.ts @@ -8,7 +8,9 @@ * @returns First 12 characters of SHA-256 hash (undefined if no username) */ export const getUserLogId = async (username?: string): Promise => { - if (!username) return undefined; + if (!username) { + return undefined; + } const buffer = await globalThis.crypto.subtle.digest('SHA-256', Buffer.from(username, 'utf-8')); const hash = Array.from(new Uint8Array(buffer)) @@ -26,7 +28,9 @@ export const getUserLogId = async (username?: string): Promise { - if (!value) return ''; + if (!value) { + return ''; + } // Remove control characters (newlines, tabs, null bytes, ANSI codes) // This prevents log injection while preserving the full header value for tracing @@ -39,11 +43,8 @@ export const sanitizeHeaderValue = (value: string | null): string => { * @returns Record with sanitized values */ export const sanitizeHeaders = (headers: Record): Record => { - return Object.entries(headers).reduce( - (acc, [key, value]) => { - acc[key] = sanitizeHeaderValue(value); - return acc; - }, - {} as Record, - ); + return Object.entries(headers).reduce((acc, [key, value]) => { + acc[key] = sanitizeHeaderValue(value); + return acc; + }, {} as Record); }; From 1c9570b3c101b7f292b178570f263aef227d3ee2 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:04 -0500 Subject: [PATCH 3/6] fix: Make author affiliations table responsive for mobile - Add horizontal scroll to TableContainer with minimum table width - Make author name, affiliation, and year column widths responsive - Table now fits on mobile with swipe-to-scroll for all columns --- .../AuthorAffiliations/AuthorAffiliations.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/AuthorAffiliations/AuthorAffiliations.tsx b/src/components/AuthorAffiliations/AuthorAffiliations.tsx index ad4f9e549..841042057 100644 --- a/src/components/AuthorAffiliations/AuthorAffiliations.tsx +++ b/src/components/AuthorAffiliations/AuthorAffiliations.tsx @@ -97,8 +97,8 @@ const AffiliationTable = (props: { query: IADSApiSearchParams; formState: AffTab } return ( - - + +
@@ -205,7 +205,7 @@ const Row = (props: { context: IGroupedAuthorAffilationData; idx: number }) => { aria-label={`select author ${ctx.authorName}`} > - + {ctx.authorName} @@ -224,7 +224,7 @@ const Row = (props: { context: IGroupedAuthorAffilationData; idx: number }) => { aria-label={`select affiliation ${aff} for author ${ctx.authorName}`} > - + {aff === NONESYMBOL ? '(none)' : aff} @@ -237,7 +237,7 @@ const Row = (props: { context: IGroupedAuthorAffilationData; idx: number }) => { {ctx.years.map((years, i) => (
  • - + {years.join(', ')} From 5b85476503811786dc5ec95224d536c02a895a16 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:05 -0500 Subject: [PATCH 4/6] fix: Improve mobile responsive styling for authenticated pages - User settings: Stack collection checkboxes vertically on mobile - Libraries: Hide owner column on mobile to prevent table overflow - Notifications: Hide updated and actions columns on mobile --- .../EmailNotifications/NotificationsPane.tsx | 10 ++++++---- src/components/Libraries/LibraryListTable.tsx | 2 +- src/pages/user/settings/application.tsx | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/EmailNotifications/NotificationsPane.tsx b/src/components/EmailNotifications/NotificationsPane.tsx index 626e3df55..58f0acfb4 100644 --- a/src/components/EmailNotifications/NotificationsPane.tsx +++ b/src/components/EmailNotifications/NotificationsPane.tsx @@ -230,7 +230,7 @@ export const NotificationsPane = () => {
  • - + {!isMobile && } {!isMobile && } @@ -245,9 +245,11 @@ export const NotificationsPane = () => { - + {!isMobile && ( + + )} {!isMobile && (
    #Name Type FrequencyUpdatedUpdatedActions
    {n.name} {n.type === 'template' ? n.template : 'query'} {n.frequency} - - + + { }); } - const defaultDatabases: typeof settings[UserDataKeys.DEFAULT_DATABASE] = []; + const defaultDatabases: (typeof settings)[UserDataKeys.DEFAULT_DATABASE] = []; for (const db of currentDatabases) { // skip ALL if (db.name === DatabaseEnum.All) { @@ -209,7 +209,7 @@ const AppSettingsPage = () => { - + All Physics From 1385e00584de9c69b1d7e624edc6df3b37918852 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:06 -0500 Subject: [PATCH 5/6] fix: Make paper form button sizes consistent Reset buttons were missing size="sm" prop, causing them to render larger than the adjacent Search buttons. - Added size="sm" to Reset buttons in all three form sections --- src/pages/paper-form.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/paper-form.tsx b/src/pages/paper-form.tsx index 459532173..debfad004 100644 --- a/src/pages/paper-form.tsx +++ b/src/pages/paper-form.tsx @@ -235,7 +235,7 @@ const JournalQueryForm = ({ onSubmit, error }: SubFormProps) => { - @@ -292,7 +292,7 @@ const ReferenceQueryForm = ({ onSubmit, error }: SubFormProps) => { - @@ -349,7 +349,7 @@ const BibcodeQueryForm = ({ onSubmit, error }: SubFormProps) => { - From e63d0111e93888bfabd98dd214ed3d438dc73db8 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:35:07 -0500 Subject: [PATCH 6/6] fix: Use descriptive tooltip for bibcode copy button The copy button tooltip now says "Copy Bibcode" instead of generic "Copy" to match the DOI copy button behavior. - Added copyLabel prop to Detail component with "Copy" default - Passed "Copy Bibcode" for the bibcode detail row --- src/components/AbstractDetails/AbstractDetails.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/AbstractDetails/AbstractDetails.tsx b/src/components/AbstractDetails/AbstractDetails.tsx index 82ce086b4..060b9296d 100644 --- a/src/components/AbstractDetails/AbstractDetails.tsx +++ b/src/components/AbstractDetails/AbstractDetails.tsx @@ -44,6 +44,7 @@ interface IDetailProps> { newTab?: boolean; value: T; copiable?: boolean; + copyLabel?: string; children?: (value: T) => ReactElement; } @@ -113,7 +114,7 @@ export const AbstractDetails = ({ doc }: IDetailsProps): ReactElement => { href={createUrlByType(doc?.bibcode, 'arxiv', arxiv?.split(':')[1])} newTab /> - + @@ -130,7 +131,7 @@ export const AbstractDetails = ({ doc }: IDetailsProps): ReactElement => { // TODO: this should take in a list of deps or the whole doc and show/hide based on that const Detail = (props: IDetailProps): ReactElement => { - const { label, href, newTab = false, value, copiable = false, children } = props; + const { label, href, newTab = false, value, copiable = false, copyLabel = 'Copy', children } = props; // show nothing if no value if (isNilOrEmpty(value)) { @@ -172,7 +173,7 @@ const Detail = (props: IDetailProps): ReactEleme ? children(value) : !href && } {copiable && ( - + )}