From 926339534e824b73d3c27c65ac3d516e53c173a4 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 9 Jun 2026 13:28:29 -0500 Subject: [PATCH 1/2] feat: add CSS Layers to 02-foundations-b Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/css-layers-foundations-b.md | 5 + packages/react/src/Details/Details.module.css | 12 ++- packages/react/src/Flash/Flash.module.css | 92 ++++++++++--------- packages/react/src/Header/Header.module.css | 66 ++++++------- packages/react/src/Heading/Heading.module.css | 26 +++--- packages/react/src/Hidden/Hidden.module.css | 20 ++-- .../InlineMessage/InlineMessage.module.css | 82 +++++++++-------- .../react/src/__tests__/css-layers.test.ts | 9 ++ .../CSSComponent/component.module.css | 16 ++-- .../UnderlinePanels.module.css | 18 ++-- .../components/ButtonReset.module.css | 30 +++--- 11 files changed, 204 insertions(+), 172 deletions(-) create mode 100644 .changeset/css-layers-foundations-b.md diff --git a/.changeset/css-layers-foundations-b.md b/.changeset/css-layers-foundations-b.md new file mode 100644 index 00000000000..ac989ce155d --- /dev/null +++ b/.changeset/css-layers-foundations-b.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Details, CSSComponent, UnderlinePanels, Flash, Header, Heading, Hidden, InlineMessage, ButtonReset: Improve custom class override behavior for component styles diff --git a/packages/react/src/Details/Details.module.css b/packages/react/src/Details/Details.module.css index a0e787b9844..2ddc3f3c93f 100644 --- a/packages/react/src/Details/Details.module.css +++ b/packages/react/src/Details/Details.module.css @@ -1,7 +1,9 @@ -.Details > summary { - list-style: none; -} +@layer primer.components.Details { + .Details > summary { + list-style: none; + } -.Details > summary::-webkit-details-marker { - display: none; + .Details > summary::-webkit-details-marker { + display: none; + } } diff --git a/packages/react/src/Flash/Flash.module.css b/packages/react/src/Flash/Flash.module.css index c16e839a4e3..45355e2a67a 100644 --- a/packages/react/src/Flash/Flash.module.css +++ b/packages/react/src/Flash/Flash.module.css @@ -1,60 +1,62 @@ -.Flash { - position: relative; - padding: var(--base-size-16); - margin-top: 0; - color: var(--fgColor-default); - border-style: solid; - border-width: var(--borderWidth-thin); - border-radius: var(--borderRadius-medium); - - &:where([data-variant='default']) { - background-color: var(--bgColor-accent-muted); - border-color: var(--borderColor-accent-muted); - - & :where(svg) { - color: var(--fgColor-accent); +@layer primer.components.Flash { + .Flash { + position: relative; + padding: var(--base-size-16); + margin-top: 0; + color: var(--fgColor-default); + border-style: solid; + border-width: var(--borderWidth-thin); + border-radius: var(--borderRadius-medium); + + &:where([data-variant='default']) { + background-color: var(--bgColor-accent-muted); + border-color: var(--borderColor-accent-muted); + + & :where(svg) { + color: var(--fgColor-accent); + } } - } - &:where([data-variant='success']) { - background-color: var(--bgColor-success-muted); - border-color: var(--borderColor-success-muted); + &:where([data-variant='success']) { + background-color: var(--bgColor-success-muted); + border-color: var(--borderColor-success-muted); - & :where(svg) { - color: var(--fgColor-success); + & :where(svg) { + color: var(--fgColor-success); + } } - } - &:where([data-variant='danger']) { - background-color: var(--bgColor-danger-muted); - border-color: var(--borderColor-danger-muted); + &:where([data-variant='danger']) { + background-color: var(--bgColor-danger-muted); + border-color: var(--borderColor-danger-muted); - & :where(svg) { - color: var(--fgColor-danger); + & :where(svg) { + color: var(--fgColor-danger); + } } - } - &:where([data-variant='warning']) { - background-color: var(--bgColor-attention-muted); - border-color: var(--borderColor-attention-muted); + &:where([data-variant='warning']) { + background-color: var(--bgColor-attention-muted); + border-color: var(--borderColor-attention-muted); - & :where(svg) { - color: var(--fgColor-attention); + & :where(svg) { + color: var(--fgColor-attention); + } } - } - &:where([data-full]) { - /* stylelint-disable-next-line primer/spacing */ - margin-top: -1px; - border-width: var(--borderWidth-thin) 0; - border-radius: 0; - } + &:where([data-full]) { + /* stylelint-disable-next-line primer/spacing */ + margin-top: -1px; + border-width: var(--borderWidth-thin) 0; + border-radius: 0; + } - & :where(p:last-child) { - margin-bottom: 0; - } + & :where(p:last-child) { + margin-bottom: 0; + } - & :where(svg) { - margin-right: var(--base-size-8); + & :where(svg) { + margin-right: var(--base-size-8); + } } } diff --git a/packages/react/src/Header/Header.module.css b/packages/react/src/Header/Header.module.css index 0d8a055fea9..3e1a0b2e7b7 100644 --- a/packages/react/src/Header/Header.module.css +++ b/packages/react/src/Header/Header.module.css @@ -1,39 +1,41 @@ -.Header { - z-index: 32; - display: flex; - padding: var(--base-size-16); - overflow: auto; - font-size: var(--text-body-size-medium); - line-height: var(--text-title-lineHeight-large); - color: var(--header-fgColor-default); - background-color: var(--header-bgColor); - align-items: center; - flex-wrap: nowrap; -} +@layer primer.components.Header { + .Header { + z-index: 32; + display: flex; + padding: var(--base-size-16); + overflow: auto; + font-size: var(--text-body-size-medium); + line-height: var(--text-title-lineHeight-large); + color: var(--header-fgColor-default); + background-color: var(--header-bgColor); + align-items: center; + flex-wrap: nowrap; + } -.HeaderItem { - display: flex; - margin-right: var(--base-size-16); - align-self: stretch; - align-items: center; - flex-wrap: nowrap; + .HeaderItem { + display: flex; + margin-right: var(--base-size-16); + align-self: stretch; + align-items: center; + flex-wrap: nowrap; - &:where([data-full]) { - flex: auto; + &:where([data-full]) { + flex: auto; + } } -} -.HeaderLink { - display: flex; - font-weight: var(--text-title-weight-large); - color: var(--header-fgColor-logo); - text-decoration: none; - white-space: nowrap; - cursor: pointer; - align-items: center; + .HeaderLink { + display: flex; + font-weight: var(--text-title-weight-large); + color: var(--header-fgColor-logo); + text-decoration: none; + white-space: nowrap; + cursor: pointer; + align-items: center; - &:hover, - &:focus { - color: var(--header-fgColor-default); + &:hover, + &:focus { + color: var(--header-fgColor-default); + } } } diff --git a/packages/react/src/Heading/Heading.module.css b/packages/react/src/Heading/Heading.module.css index df66c322d6b..4cd58293fff 100644 --- a/packages/react/src/Heading/Heading.module.css +++ b/packages/react/src/Heading/Heading.module.css @@ -1,17 +1,19 @@ -:where(.Heading) { - margin: 0; - font-size: var(--text-title-size-large); - font-weight: var(--base-text-weight-semibold); +@layer primer.components.Heading { + :where(.Heading) { + margin: 0; + font-size: var(--text-title-size-large); + font-weight: var(--base-text-weight-semibold); - &:where([data-variant='large']) { - font: var(--text-title-shorthand-large); - } + &:where([data-variant='large']) { + font: var(--text-title-shorthand-large); + } - &:where([data-variant='medium']) { - font: var(--text-title-shorthand-medium); - } + &:where([data-variant='medium']) { + font: var(--text-title-shorthand-medium); + } - &:where([data-variant='small']) { - font: var(--text-title-shorthand-small); + &:where([data-variant='small']) { + font: var(--text-title-shorthand-small); + } } } diff --git a/packages/react/src/Hidden/Hidden.module.css b/packages/react/src/Hidden/Hidden.module.css index b43848f3526..6f713446b1e 100644 --- a/packages/react/src/Hidden/Hidden.module.css +++ b/packages/react/src/Hidden/Hidden.module.css @@ -1,13 +1,15 @@ -.Hidden { - @media screen and (--viewportRange-narrow) { - display: var(--hiddenDisplay-narrow, block); - } +@layer primer.components.Hidden { + .Hidden { + @media screen and (--viewportRange-narrow) { + display: var(--hiddenDisplay-narrow, block); + } - @media screen and (--viewportRange-regular) { - display: var(--hiddenDisplay-regular, block); - } + @media screen and (--viewportRange-regular) { + display: var(--hiddenDisplay-regular, block); + } - @media screen and (--viewportRange-wide) { - display: var(--hiddenDisplay-wide, block); + @media screen and (--viewportRange-wide) { + display: var(--hiddenDisplay-wide, block); + } } } diff --git a/packages/react/src/InlineMessage/InlineMessage.module.css b/packages/react/src/InlineMessage/InlineMessage.module.css index cff5c2839df..82f95bc9258 100644 --- a/packages/react/src/InlineMessage/InlineMessage.module.css +++ b/packages/react/src/InlineMessage/InlineMessage.module.css @@ -1,44 +1,46 @@ -.InlineMessage { - display: grid; - /* stylelint-disable-next-line primer/typography */ - font-size: var(--inline-message-fontSize); - /* stylelint-disable-next-line primer/typography */ - line-height: var(--inline-message-lineHeight); - /* stylelint-disable-next-line primer/colors */ - color: var(--inline-message-fgColor); - column-gap: 0.5rem; - grid-template-columns: auto 1fr; - align-items: start; - - --inline-message-fgColor: var(--fgColor-default); - - &[data-size='small'] { - --inline-message-fontSize: var(--text-body-size-small); - --inline-message-lineHeight: var(--text-body-lineHeight-small, 1.6666); +@layer primer.components.InlineMessage { + .InlineMessage { + display: grid; + /* stylelint-disable-next-line primer/typography */ + font-size: var(--inline-message-fontSize); + /* stylelint-disable-next-line primer/typography */ + line-height: var(--inline-message-lineHeight); + /* stylelint-disable-next-line primer/colors */ + color: var(--inline-message-fgColor); + column-gap: 0.5rem; + grid-template-columns: auto 1fr; + align-items: start; + + --inline-message-fgColor: var(--fgColor-default); + + &[data-size='small'] { + --inline-message-fontSize: var(--text-body-size-small); + --inline-message-lineHeight: var(--text-body-lineHeight-small, 1.6666); + } + + &[data-size='medium'] { + --inline-message-fontSize: var(--text-body-size-medium); + --inline-message-lineHeight: var(--text-body-lineHeight-medium, 1.4285); + } + + &[data-variant='warning'] { + --inline-message-fgColor: var(--fgColor-attention); + } + + &[data-variant='critical'] { + --inline-message-fgColor: var(--fgColor-danger); + } + + &[data-variant='success'] { + --inline-message-fgColor: var(--fgColor-success); + } + + &[data-variant='unavailable'] { + --inline-message-fgColor: var(--fgColor-muted); + } } - &[data-size='medium'] { - --inline-message-fontSize: var(--text-body-size-medium); - --inline-message-lineHeight: var(--text-body-lineHeight-medium, 1.4285); + .InlineMessageIcon { + min-height: calc(var(--inline-message-lineHeight) * var(--inline-message-fontSize)); } - - &[data-variant='warning'] { - --inline-message-fgColor: var(--fgColor-attention); - } - - &[data-variant='critical'] { - --inline-message-fgColor: var(--fgColor-danger); - } - - &[data-variant='success'] { - --inline-message-fgColor: var(--fgColor-success); - } - - &[data-variant='unavailable'] { - --inline-message-fgColor: var(--fgColor-muted); - } -} - -.InlineMessageIcon { - min-height: calc(var(--inline-message-lineHeight) * var(--inline-message-fontSize)); } diff --git a/packages/react/src/__tests__/css-layers.test.ts b/packages/react/src/__tests__/css-layers.test.ts index 1dc0bf4f778..3d548f05f0f 100644 --- a/packages/react/src/__tests__/css-layers.test.ts +++ b/packages/react/src/__tests__/css-layers.test.ts @@ -16,6 +16,15 @@ const allowlist = new Set([ path.resolve(import.meta.dirname, '../CircleBadge/CircleBadge.module.css'), path.resolve(import.meta.dirname, '../deprecated/FilteredSearch/FilteredSearch.module.css'), path.resolve(import.meta.dirname, '../deprecated/UnderlineNav/UnderlineNav.module.css'), + path.resolve(import.meta.dirname, '../Details/Details.module.css'), + path.resolve(import.meta.dirname, '../experimental/CSSComponent/component.module.css'), + path.resolve(import.meta.dirname, '../experimental/UnderlinePanels/UnderlinePanels.module.css'), + path.resolve(import.meta.dirname, '../Flash/Flash.module.css'), + path.resolve(import.meta.dirname, '../Header/Header.module.css'), + path.resolve(import.meta.dirname, '../Heading/Heading.module.css'), + path.resolve(import.meta.dirname, '../Hidden/Hidden.module.css'), + path.resolve(import.meta.dirname, '../InlineMessage/InlineMessage.module.css'), + path.resolve(import.meta.dirname, '../internal/components/ButtonReset.module.css'), ]) const files = Array.from(allowlist).map(file => { return [path.relative(path.resolve(import.meta.dirname, '..'), file), file] diff --git a/packages/react/src/experimental/CSSComponent/component.module.css b/packages/react/src/experimental/CSSComponent/component.module.css index 92283f5b496..143a1e652ec 100644 --- a/packages/react/src/experimental/CSSComponent/component.module.css +++ b/packages/react/src/experimental/CSSComponent/component.module.css @@ -1,8 +1,10 @@ -.Component { - width: fit-content; - /* stylelint-disable-next-line primer/spacing */ - padding: var(--control-xsmall-paddingBlock) var(--control-xsmall-paddingInline-normal); - color: var(--fgColor-muted); - background-color: var(--bgColor-default); - border: var(--borderWidth-thin) solid var(--borderColor-default); +@layer primer.components.CSSComponent { + .Component { + width: fit-content; + /* stylelint-disable-next-line primer/spacing */ + padding: var(--control-xsmall-paddingBlock) var(--control-xsmall-paddingInline-normal); + color: var(--fgColor-muted); + background-color: var(--bgColor-default); + border: var(--borderWidth-thin) solid var(--borderColor-default); + } } diff --git a/packages/react/src/experimental/UnderlinePanels/UnderlinePanels.module.css b/packages/react/src/experimental/UnderlinePanels/UnderlinePanels.module.css index 9e4905c2c7f..c8d3acecfa3 100644 --- a/packages/react/src/experimental/UnderlinePanels/UnderlinePanels.module.css +++ b/packages/react/src/experimental/UnderlinePanels/UnderlinePanels.module.css @@ -1,10 +1,12 @@ -.StyledUnderlineWrapper { - width: 100%; - overflow-x: auto; - overflow-y: hidden; - -webkit-overflow-scrolling: auto; -} +@layer primer.components.UnderlinePanels { + .StyledUnderlineWrapper { + width: 100%; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: auto; + } -.StyledUnderlineWrapper[data-icons-visible='false'] [data-component='icon'] { - display: none; + .StyledUnderlineWrapper[data-icons-visible='false'] [data-component='icon'] { + display: none; + } } diff --git a/packages/react/src/internal/components/ButtonReset.module.css b/packages/react/src/internal/components/ButtonReset.module.css index c291a5e01c5..fc844fec74b 100644 --- a/packages/react/src/internal/components/ButtonReset.module.css +++ b/packages/react/src/internal/components/ButtonReset.module.css @@ -1,17 +1,19 @@ -.ButtonReset { - margin: 0; - display: inline-flex; - padding: 0; - border: 0; - appearance: none; - background: none; - cursor: pointer; - text-align: start; - font: inherit; - color: inherit; - align-items: center; - - &::-moz-focus-inner { +@layer primer.components.ButtonReset { + .ButtonReset { + margin: 0; + display: inline-flex; + padding: 0; border: 0; + appearance: none; + background: none; + cursor: pointer; + text-align: start; + font: inherit; + color: inherit; + align-items: center; + + &::-moz-focus-inner { + border: 0; + } } } From d91dd19c87048ee792e7a39221d3b5dcc1e85733 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 9 Jun 2026 15:12:40 -0500 Subject: [PATCH 2/2] chore: standardize CSS layer changeset wording Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/css-layers-foundations-b.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/css-layers-foundations-b.md b/.changeset/css-layers-foundations-b.md index ac989ce155d..208f37ded08 100644 --- a/.changeset/css-layers-foundations-b.md +++ b/.changeset/css-layers-foundations-b.md @@ -2,4 +2,4 @@ '@primer/react': patch --- -Details, CSSComponent, UnderlinePanels, Flash, Header, Heading, Hidden, InlineMessage, ButtonReset: Improve custom class override behavior for component styles +Details, CSSComponent, UnderlinePanels, Flash, Header, Heading, Hidden, InlineMessage, ButtonReset: Add CSS layer support for component styles