1
0
mirror of synced 2025-12-19 09:57:42 -05:00

Replace sx and inline style declarations with CSS modules (#57848)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Kevin Heis
2025-10-08 11:07:23 -07:00
committed by GitHub
parent b15df1140b
commit f5df15a5c8
36 changed files with 246 additions and 134 deletions

View File

@@ -0,0 +1,16 @@
.eventItem {
margin-bottom: 3rem;
}
.eventAction {
font-style: normal;
}
.eventDetail {
margin-left: 1rem;
font-style: normal;
}
.eventDescription {
margin-left: 1rem;
}

View File

@@ -4,6 +4,8 @@ import { HeadingLink } from '@/frame/components/article/HeadingLink'
import { useTranslation } from '@/languages/components/useTranslation'
import type { AuditLogEventT } from '../types'
import styles from './GroupedEvents.module.scss'
type Props = {
auditLogEvents: AuditLogEventT[]
category: string
@@ -47,15 +49,15 @@ export default function GroupedEvents({ auditLogEvents, category, categoryNote }
)}
<div>
{auditLogEvents.map((event) => (
<div key={event.action} style={{ marginBottom: '3rem' }}>
<div key={event.action} className={styles.eventItem}>
<dl>
<dt style={{ fontStyle: 'normal' }}>
<dt className={styles.eventAction}>
<code>{event.action}</code>
</dt>
<dd>{event.description}</dd>
<dt style={{ marginLeft: '1rem', fontStyle: 'normal' }}>{t('fields')}</dt>
<dd style={{ marginLeft: '1rem' }}>
<dt className={styles.eventDetail}>{t('fields')}</dt>
<dd className={styles.eventDescription}>
{event.fields
? event.fields.map((field, index) => (
<span key={field}>
@@ -68,8 +70,8 @@ export default function GroupedEvents({ auditLogEvents, category, categoryNote }
{event.docs_reference_links && event.docs_reference_links !== 'N/A' && (
<>
<dt style={{ marginLeft: '1rem', fontStyle: 'normal' }}>{t('reference')}</dt>
<dd style={{ marginLeft: '1rem' }}>{renderReferenceLinks(event)}</dd>
<dt className={styles.eventDetail}>{t('reference')}</dt>
<dd className={styles.eventDescription}>{renderReferenceLinks(event)}</dd>
</>
)}
</dl>

View File

@@ -0,0 +1,3 @@
.mainContent {
scroll-margin-top: 5rem;
}

View File

@@ -15,6 +15,8 @@ import { useLanguages } from '@/languages/components/LanguagesContext'
import { ClientSideLanguageRedirect } from './ClientSideLanguageRedirect'
import { SearchOverlayContextProvider } from '@/search/components/context/SearchOverlayContext'
import styles from './DefaultLayout.module.scss'
const MINIMAL_RENDER = Boolean(JSON.parse(process.env.MINIMAL_RENDER || 'false'))
type Props = { children?: React.ReactNode }
@@ -51,7 +53,7 @@ export const DefaultLayout = (props: Props) => {
<Breadcrumbs />
</div>
<main id="main-content" style={{ scrollMarginTop: '5rem' }}>
<main id="main-content" className={styles.mainContent}>
{props.children}
</main>
</div>
@@ -150,7 +152,7 @@ export const DefaultLayout = (props: Props) => {
{/* Need to set an explicit height for sticky elements since we also
set overflow to auto */}
<div className="flex-column flex-1 min-width-0">
<main id="main-content" style={{ scrollMarginTop: '5rem' }}>
<main id="main-content" className={styles.mainContent}>
<DeprecationBanner />
<RestBanner />

View File

@@ -0,0 +1,3 @@
.logoContainer {
z-index: 3;
}

View File

@@ -5,6 +5,8 @@ import { useRouter } from 'next/router'
import { MarkGithubIcon, CommentDiscussionIcon } from '@primer/octicons-react'
import { Lead } from '@/frame/components/ui/Lead'
import styles from './GenericError.module.scss'
export function GenericError() {
return (
<div className="min-h-screen d-flex flex-column">
@@ -44,7 +46,7 @@ export const SimpleHeader = () => {
role="banner"
aria-label="Main"
>
<div className="d-flex flex-items-center" style={{ zIndex: 3 }} id="github-logo-mobile">
<div className={`d-flex flex-items-center ${styles.logoContainer}`} id="github-logo-mobile">
<Link href={`/${router.locale}`} aria-hidden="true" tabIndex={-1}>
<MarkGithubIcon size={32} className="color-fg-default" />
</Link>

View File

@@ -57,3 +57,7 @@
padding: 0 !important;
}
}
.headerContainer {
row-gap: 1rem;
}

View File

@@ -156,11 +156,10 @@ export const Header = () => {
aria-label="Main"
>
<div
className="d-flex flex-justify-between p-2 flex-items-center flex-wrap"
style={{
// In the rare case of header overflow, create a pleasant gap between the rows
rowGap: '1rem',
}}
className={cx(
'd-flex flex-justify-between p-2 flex-items-center flex-wrap',
styles.headerContainer,
)}
data-testid="desktop-header"
>
<div

View File

@@ -10,3 +10,29 @@
visibility: hidden !important;
}
}
// The ... menu button when the smaller width search UI is open. Since the search
// UI is open, we don't show the button at smaller widths but we do show it as
// the browser width increases to md, and then at lg and above widths we hide
// the button again since the pickers and sign-up button are shown in the header.
.menuButtonSearchOpen {
margin-left: 0.5rem;
display: none;
@include breakpoint(md) {
display: inline-block;
margin-left: 0.25rem;
}
}
// The ... menu button when the smaller width search UI is closed, the button is
// shown up to lg. At lg and above we don't show the button since the pickers
// and sign-up button are shown in the header.
.menuButtonSearchClosed {
margin-left: 1rem;
@include breakpoint(lg) {
margin-left: 0;
display: none;
}
}

View File

@@ -55,36 +55,8 @@ export function HeaderSearchAndWidgets({ width, isSearchOpen, SearchButton }: Pr
data-testid="mobile-menu"
icon={KebabHorizontalIcon}
aria-label={t('header.open_menu_label')}
sx={
isSearchOpen
? // The ... menu button when the smaller width search UI is open. Since the search
// UI is open, we don't show the button at smaller widths but we do show it as
// the browser width increases to md, and then at lg and above widths we hide
// the button again since the pickers and sign-up button are shown in the header.
{
marginLeft: '8px',
display: 'none',
// breakpoint(md)
'@media (min-width: 768px)': {
display: 'inline-block',
marginLeft: '4px',
},
// breakpoint(lg)
'@media (min-width: 1012px)': {
display: 'inline-block',
marginLeft: '4px',
},
}
: // The ... menu button when the smaller width search UI is closed, the button is
// shown up to md. At lg and above we don't show the button since the pickers
// and sign-up button are shown in the header.
{
marginLeft: '16px',
'@media (min-width: 1012px)': {
marginLeft: '0',
display: 'none',
},
}
className={
isSearchOpen ? styles.menuButtonSearchOpen : styles.menuButtonSearchClosed
}
/>
</ActionMenu.Anchor>

View File

@@ -0,0 +1,33 @@
.sidebarFull {
// Fixed width for consistent sidebar layout
width: 326px;
// 65px accounts for the header height
height: calc(100vh - 65px);
top: 65px;
}
.sidebarContentFull {
width: 326px;
// 175px accounts for header (65px) + sidebar header (110px)
height: calc(100vh - 175px);
}
.sidebarContentOverlay {
// Padding to account for header + sidebar header so content isn't cut off
padding-bottom: 185px;
}
.sidebarContentOverlayRest {
// Extra padding for REST pages (includes API version picker)
padding-bottom: 250px;
}
.sidebarContentFullWithPadding {
// Padding to account for header + sidebar header so content isn't cut off
padding-bottom: 185px;
}
.sidebarContentFullWithPaddingRest {
// Extra padding for REST pages (includes API version picker)
padding-bottom: 250px;
}

View File

@@ -8,6 +8,8 @@ import { AllProductsLink } from './AllProductsLink'
import { ApiVersionPicker } from '@/rest/components/ApiVersionPicker'
import { Link } from '@/frame/components/Link'
import styles from './SidebarNav.module.scss'
type Props = {
variant?: 'full' | 'overlay'
}
@@ -22,20 +24,15 @@ export const SidebarNav = ({ variant = 'full' }: Props) => {
// Early access does not have a "home page" unless it's local dev
(process.env.NODE_ENV === 'development' || currentProduct.id !== 'early-access')
// we need to roughly account for the site header height plus the height of
// the side nav header (which is taller when we show the API version picker)
// so we don't cut off the bottom of the sidebar
const sidebarPaddingBottom = isRestPage ? '250px' : '185px'
const isSearch = currentProduct?.id === 'search'
return (
<div
data-container="nav"
className={cx(variant === 'full' ? 'position-sticky d-none border-right d-xxl-block' : '')}
style={
variant === 'full' ? { width: 326, height: 'calc(100vh - 65px)', top: '65px' } : undefined
}
className={cx(
variant === 'full' ? 'position-sticky d-none border-right d-xxl-block' : '',
variant === 'full' && styles.sidebarFull,
)}
>
<nav
aria-labelledby="allproducts-menu"
@@ -68,12 +65,16 @@ export const SidebarNav = ({ variant = 'full' }: Props) => {
? 'width-full d-xxl-none'
: 'border-right d-none d-xxl-block overflow-y-auto',
'bg-primary flex-shrink-0',
)}
style={
variant === 'overlay'
? { paddingBottom: sidebarPaddingBottom }
: { width: 326, height: 'calc(100vh - 175px)', paddingBottom: sidebarPaddingBottom }
}
? isRestPage
? styles.sidebarContentOverlayRest
: styles.sidebarContentOverlay
: styles.sidebarContentFull,
variant === 'full' &&
(isRestPage
? styles.sidebarContentFullWithPaddingRest
: styles.sidebarContentFullWithPadding),
)}
role="region"
aria-label="Page navigation content"
>

View File

@@ -32,3 +32,14 @@
}
}
}
.codeBlockContainer {
position: relative;
}
.copyButton {
position: absolute;
right: -0.7rem;
top: -0.7rem;
z-index: 1;
}

View File

@@ -12,6 +12,8 @@ import useCopyClipboard from '@/rest/components/useClipboard'
import { EventType } from '@/events/types'
import { sendEvent } from '@/events/components/events'
import styles from './MarkdownContent.module.scss'
export type MarkdownContentPropsT = {
children: string
className?: string
@@ -72,11 +74,11 @@ export const UnrenderedMarkdownContent = ({
}
return (
<div style={{ position: 'relative' }}>
<div className={styles.codeBlockContainer}>
<IconButton
size="small"
icon={isCopied ? CheckIcon : CopyIcon}
className="btn-octicon"
className={cx('btn-octicon', styles.copyButton)}
aria-label={getAriaLabel()}
onClick={async () => {
await copyToClipboard()
@@ -88,12 +90,6 @@ export const UnrenderedMarkdownContent = ({
eventGroupId: eventGroupId,
})
}}
sx={{
position: 'absolute',
right: '-.7rem',
top: '-.7rem',
zIndex: 1,
}}
></IconButton>
<code {...props}>{props.children}</code>
</div>

View File

@@ -59,13 +59,7 @@ export function MiniTocs({ miniTocItems }: MiniTocsPropsT) {
<Heading
as="h2"
id="in-this-article"
className="mb-1 ml-3"
sx={{
'@media (min-width: 1012px) and (max-width: 1400px)': {
marginTop: '2rem',
},
fontSize: 1,
}}
className={cx('mb-1 ml-3', styles.heading)}
aria-label={t('miniToc')}
>
{t('miniToc')}

View File

@@ -1,3 +1,7 @@
@import "@primer/css/support/variables/layout.scss";
@import "@primer/css/support/mixins/layout.scss";
@import "src/frame/stylesheets/breakpoint-xxl.scss";
.indentNested {
padding-inline-start: 0;
}
@@ -18,3 +22,15 @@
overflow-wrap: anywhere;
}
}
.heading {
font-size: var(--text-body-size-small);
@include breakpoint(lg) {
margin-top: 2rem;
@include breakpoint-xxl {
margin-top: 0;
}
}
}

View File

@@ -0,0 +1,6 @@
.permissionsBox {
border-radius: 0.625rem;
border-style: solid;
border-color: var(--borderColor-default);
padding: var(--base-size-16);
}

View File

@@ -1,8 +1,9 @@
import { Box } from '@primer/react'
import { PersonIcon, BriefcaseIcon } from '@primer/octicons-react'
import { useTranslation } from '@/languages/components/useTranslation'
import styles from './PermissionsStatement.module.scss'
type Props = {
product?: string
permissions?: string
@@ -12,7 +13,7 @@ export function PermissionsStatement({ product, permissions }: Props) {
const { t } = useTranslation('pages')
if (!permissions && !product) return null
return (
<Box sx={{ borderRadius: 10, borderStyle: 'solid', borderColor: 'border.default', p: 3 }}>
<div className={styles.permissionsBox}>
<div data-search="hide" data-testid="permissions-callout">
<div className="mb-3 d-inline-block">
<h2 className="f4">{t('permissions_callout_title')}</h2>
@@ -30,6 +31,6 @@ export function PermissionsStatement({ product, permissions }: Props) {
</div>
)}
</div>
</Box>
</div>
)
}

View File

@@ -16,3 +16,8 @@
outline-offset: -2px;
box-shadow: 0 0 0 4px inset; // A little wider for the border radius :shrug:
}
.scrollButton {
width: 2.5rem;
height: 2.5rem;
}

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import cx from 'classnames'
import { ChevronUpIcon } from '@primer/octicons-react'
import styles from './ScrollButton.module.scss'
const { transition200, opacity0, opacity100, customFocus } = styles
@@ -70,8 +71,8 @@ export const ScrollButton = ({ className, ariaLabel }: ScrollButtonPropsT) => {
'tooltipped tooltipped-n tooltipped-no-delay btn circle border-1',
'd-flex flex-items-center flex-justify-center',
customFocus,
styles.scrollButton,
)}
style={{ width: 40, height: 40 }}
aria-label={ariaLabel}
>
<ChevronUpIcon />

View File

@@ -0,0 +1,3 @@
.explorerIframe {
height: 45rem;
}

View File

@@ -9,6 +9,8 @@ import {
} from '@/automated-pipelines/components/AutomatedPageContext'
import { useEffect, useRef } from 'react'
import styles from './explorer.module.scss'
type Props = {
mainContext: MainContextT
graphqlExplorerUrl: string
@@ -34,8 +36,7 @@ export default function GQLExplorer({
<div>
<iframe
ref={graphiqlRef}
style={{ height: 715 }}
className="border width-full"
className={`border width-full ${styles.explorerIframe}`}
scrolling="no"
src={graphqlExplorerUrl}
title="GitHub GraphQL API"

View File

@@ -0,0 +1,12 @@
.menuButton {
height: auto;
text-align: left;
span:first-child {
display: inline;
}
}
.languageLabel {
white-space: pre-wrap;
}

View File

@@ -6,6 +6,8 @@ import { useTranslation } from '@/languages/components/useTranslation'
import { useUserLanguage } from '@/languages/components/useUserLanguage'
import { ActionList, ActionMenu, IconButton } from '@primer/react'
import styles from './LanguagePicker.module.scss'
type Props = {
xs?: boolean
mediumOrLower?: boolean
@@ -79,15 +81,10 @@ export const LanguagePicker = ({ xs, mediumOrLower }: Props) => {
<ActionMenu.Anchor>
<ActionMenu.Button
variant="invisible"
className="color-fg-default width-full"
className={`color-fg-default width-full ${styles.menuButton}`}
aria-label={`Select language: current language is ${selectedLang.name}`}
sx={{
height: 'auto',
textAlign: 'left',
'span:first-child': { display: 'inline' },
}}
>
<span style={{ whiteSpace: 'pre-wrap' }}>{t('language_picker_label') + '\n'}</span>
<span className={styles.languageLabel}>{t('language_picker_label') + '\n'}</span>
<span className="color-fg-muted text-normal f6">{selectedLang.name}</span>
</ActionMenu.Button>
</ActionMenu.Anchor>

View File

@@ -6,6 +6,8 @@ import { PatchNotes } from './PatchNotes'
import { Link } from '@/frame/components/Link'
import { CurrentVersion, ReleaseNotePatch, GHESMessage } from './types'
import styles from './PatchNotes.module.scss'
type Props = {
patch: ReleaseNotePatch
currentVersion: CurrentVersion
@@ -26,8 +28,7 @@ export function GHESReleaseNotePatch({
return (
<div ref={containerRef} className="mb-10 pb-6" id={patch.version}>
<header
style={{ zIndex: 1, marginTop: -1 }}
className="container-md border-top border-bottom px-3 pt-4 pb-2"
className={`container-md border-top border-bottom px-3 pt-4 pb-2 ${styles.patchHeader}`}
>
<div className="d-flex flex-justify-between flex-wrap">
<h2 className="border-bottom-0 m-0 p-0 mt-2">
@@ -36,8 +37,7 @@ export function GHESReleaseNotePatch({
{patch.release_candidate && (
<span
className="IssueLabel color-bg-attention-emphasis color-fg-on-emphasis ml-3 flex-items-center d-inline-flex"
style={{ whiteSpace: 'pre' }}
className={`IssueLabel color-bg-attention-emphasis color-fg-on-emphasis ml-3 flex-items-center d-inline-flex ${styles.releaseLabel}`}
>
Release Candidate
</span>

View File

@@ -14,3 +14,12 @@
top: 65px;
}
}
.patchHeader {
z-index: 1;
margin-top: -1px;
}
.releaseLabel {
white-space: pre;
}

View File

@@ -30,6 +30,7 @@
.responseCodeBlock {
min-height: 120px;
max-height: 60vh;
}
.segmentedControl {

View File

@@ -100,7 +100,6 @@ export function RestCodeSamples({ operation, slug, heading }: Props) {
const [selectedLanguage, setSelectedLanguage] = useState(languageSelectOptions[0])
const [selectedExample, setSelectedExample] = useState(exampleSelectOptions[0])
const [selectedResponse, setSelectedResponse] = useState(responseSelectOptions[0])
const [responseMaxHeight, setResponseMaxHeight] = useState(0)
const isSingleExample = languageExamples.length === 1
const displayedExample: ExampleT = languageExamples[selectedExample.languageIndex]
@@ -118,19 +117,6 @@ export function RestCodeSamples({ operation, slug, heading }: Props) {
Cookies.set('codeSampleLanguagePreferred', languageKey)
}
const handleResponseResize = () => {
if (requestCodeExample.current) {
const requestCodeHeight = requestCodeExample.current.clientHeight || 0
const { innerHeight: height } = window
if (responseCodeExample) {
// 520 pixels roughly accounts for the space taken up by the
// nav bar, headers, language picker, method section, and response
// picker
setResponseMaxHeight(height - requestCodeHeight - 520)
}
}
}
// Change the language based on cookies
useEffect(() => {
// If the user previously selected a language preference and the language
@@ -152,7 +138,6 @@ export function RestCodeSamples({ operation, slug, heading }: Props) {
// (ClientSideHighlightJS) will have already handled highlighting
if (reqElem && !firstRender.current) {
highlightElement(reqElem)
handleResponseResize()
}
}, [selectedLanguage])
@@ -196,15 +181,6 @@ export function RestCodeSamples({ operation, slug, heading }: Props) {
}
}, [])
// Handle the resizing of the response section when the window is resized
useEffect(() => {
handleResponseResize()
window.addEventListener('resize', handleResponseResize)
return () => {
window.removeEventListener('resize', handleResponseResize)
}
})
const [isCopied, setCopied] = useClipboard(displayedExample[selectedLanguage] as string, {
successDuration: 1400,
})
@@ -360,7 +336,6 @@ export function RestCodeSamples({ operation, slug, heading }: Props) {
'border-top rounded-1 my-0',
)}
data-highlight={'json'}
style={{ maxHeight: responseMaxHeight }}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
>

View File

@@ -5,3 +5,7 @@
.codeBlock code:not(td *) {
word-break: break-word;
}
.stickyCodeColumn {
top: 6.5em;
}

View File

@@ -102,8 +102,10 @@ export function RestOperation({ operation }: Props) {
)}
</div>
<div
className="col-md-12 col-lg-6 position-sticky flex-self-start"
style={{ top: '6.5em' }}
className={cx(
'col-md-12 col-lg-6 position-sticky flex-self-start',
styles.stickyCodeColumn,
)}
>
{hasCodeSamples && (
<RestCodeSamples

View File

@@ -3,3 +3,10 @@
display: none;
}
}
.linkItem {
&:hover {
text-decoration: none;
text-align: left;
}
}

View File

@@ -1,5 +1,6 @@
import { ActionList } from '@primer/react'
import { ReactNode } from 'react'
import cx from 'classnames'
import { PickerItem } from './Picker'
@@ -34,13 +35,10 @@ export const Fields = (fieldProps: {
// variant span box in front of it. To date there isn't a possibility to have
// an ActionMenu in Primer that allow non-selection variant items with selection
// variant items
className={(item.extra?.arrow || item.extra?.info) && styles.extrasDisplay}
sx={{
':hover': {
textDecoration: 'none',
textAlign: 'left',
},
}}
className={cx(
(item.extra?.arrow || item.extra?.info) && styles.extrasDisplay,
styles.linkItem,
)}
role={item.extra?.arrow || item.extra?.info ? 'menuitem' : 'menuitemradio'}
>
{renderItem ? renderItem(item) : item.text}

View File

@@ -0,0 +1,12 @@
.menuButton {
height: auto;
text-align: left;
span:first-child {
display: inline;
}
}
.pickerLabel {
white-space: pre-wrap;
}

View File

@@ -4,6 +4,7 @@ import { ActionMenu } from '@primer/react'
import { AnchorAlignment } from '@primer/behaviors'
import { Fields } from './Fields'
import styles from './Picker.module.scss'
interface Props {
items: PickerItem[]
@@ -47,14 +48,9 @@ export const Picker = ({
<ActionMenu.Button
aria-label={ariaLabel}
variant={buttonBorder ? 'default' : 'invisible'}
className="color-fg-default width-full p-1 pl-2 pr-2"
sx={{
height: 'auto',
textAlign: 'left',
'span:first-child': { display: 'inline' },
}}
className={`color-fg-default width-full p-1 pl-2 pr-2 ${styles.menuButton}`}
>
{pickerLabel && <span style={{ whiteSpace: 'pre-wrap' }}>{`${pickerLabel}`}</span>}
{pickerLabel && <span className={styles.pickerLabel}>{`${pickerLabel}`}</span>}
<span
className={`f${descriptionFontSize} color-fg-muted text-normal`}
data-testid={dataTestId}

View File

@@ -157,9 +157,7 @@ export function Webhook({ webhook }: Props) {
<Flash className="mb-5" variant="danger">
<p>{t('action_type_switch_error')}</p>
<p>
<code className="f6" style={{ background: 'none' }}>
{error.toString()}
</code>
<code className={`f6 ${styles.errorCode}`}>{error.toString()}</code>
</p>
</Flash>
)}

View File

@@ -12,3 +12,7 @@
padding: 8px 8px 16px;
}
}
.errorCode {
background: none;
}