diff --git a/.changeset/swift-dryers-invite.md b/.changeset/swift-dryers-invite.md new file mode 100644 index 0000000000..79282a053d --- /dev/null +++ b/.changeset/swift-dryers-invite.md @@ -0,0 +1,5 @@ +--- +"@primer/view-components": patch +--- + +Move `Overlay` styles to PVC diff --git a/app/components/primer/alpha/dialog.pcss b/app/components/primer/alpha/dialog.pcss new file mode 100644 index 0000000000..86a8da11e0 --- /dev/null +++ b/app/components/primer/alpha/dialog.pcss @@ -0,0 +1,484 @@ +/* Overlay */ + +.Overlay--hidden { + display: none !important; +} + +.Overlay--visibilityHidden { + height: 0; + overflow: hidden; + visibility: hidden; + opacity: 0; +} + +.Overlay { + display: flex; + width: min(var(--overlay-width), 100vw - 2rem); + min-width: 192px; + max-height: min(calc(100vh - 2rem), var(--overlay-height)); + white-space: normal; + flex-direction: column; + background-color: var(--color-canvas-overlay); + border-radius: var(--primer-borderRadius-large, 12px); + box-shadow: var(--color-overlay-shadow); + opacity: 1; + + &.Overlay--size-auto { + min-width: 192px; + max-width: calc(100vw - 2rem); + max-height: calc(100vh - 2rem); + } + + &.Overlay--size-full { + width: 100vw; + height: 100vh; + } + + &.Overlay--size-xsmall { + --overlay-width: 192px; + + max-height: calc(100vh - 2rem); + } + + &.Overlay--size-small { + --overlay-height: 256px; + --overlay-width: 320px; + } + + &.Overlay--size-small-portrait { + --overlay-height: 432px; + --overlay-width: 320px; + } + + &.Overlay--size-medium { + --overlay-height: 320px; + --overlay-width: 480px; + } + + &.Overlay--size-medium-portrait { + --overlay-height: 600px; + --overlay-width: 480px; + } + + &.Overlay--size-large { + --overlay-height: 432px; + --overlay-width: 640px; + } + + &.Overlay--size-xlarge { + --overlay-height: 600px; + --overlay-width: 960px; + } + + &.Overlay--height-auto { + height: auto; + } + + /* start deprecate in favor of Alpha::Dialog */ + &.Overlay--height-xsmall { + height: min(192px, 100vh - 2rem); + } + + &.Overlay--height-small { + height: min(256px, 100vh - 2rem); + } + + &.Overlay--height-medium { + height: min(320px, 100vh - 2rem); + } + + &.Overlay--height-large { + height: min(432px, 100vh - 2rem); + } + + &.Overlay--height-xlarge { + height: min(600px, 100vh - 2rem); + } + + &.Overlay--width-auto { + width: auto; + } + + &.Overlay--width-small { + width: min(256px, 100vw - 2rem); + } + + &.Overlay--width-medium { + width: min(320px, 100vw - 2rem); + } + + &.Overlay--width-large { + width: min(480px, 100vw - 2rem); + } + + &.Overlay--width-xlarge { + width: min(640px, 100vw - 2rem); + } + + &.Overlay--width-xxlarge { + width: min(960px, 100vw - 2rem); + } + + /* end deprecate */ + + &.Overlay--motion-scaleFade { + @media screen and (prefers-reduced-motion: no-preference) { + animation: 200ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-scaleFade; + } + } +} + +@keyframes Overlay--motion-scaleFade { + 0% { + opacity: 0; + transform: scale(0.5); + } + + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* for
element that wraps entire contents of overlay */ +.Overlay-form { + display: flex; + overflow: auto; + flex-direction: column; + flex-grow: 1; +} + +.Overlay-header { + z-index: 1; + display: flex; + flex-direction: column; + + &.Overlay-header--divided { + padding-bottom: var(--primer-stack-padding-condensed, 8px); + box-shadow: inset 0 calc(var(--primer-borderWidth-thin, 1px) * -1) var(--color-border-default); + + & + .Overlay-body { + padding-top: var(--primer-stack-padding-normal, 16px); + } + } + + &.Overlay-header--large { + & .Overlay-headerContentWrap { + & .Overlay-titleWrap { + gap: var(--primer-stack-gap-condensed, 8px); + + & .Overlay-title { + font-size: var(--primer-text-title-size-medium, 20px); + } + + & .Overlay-description { + font-size: var(--primer-text-body-size-medium, 14px); + } + } + } + } + + & .Overlay-headerContentWrap { + display: flex; + align-items: flex-start; + gap: var(--primer-stack-gap-condensed, 8px); + padding: var(--primer-stack-gap-condensed, 8px) var(--primer-stack-gap-condensed, 8px) 0 var(--primer-stack-gap-condensed, 8px); + + & .Overlay-actionWrap { + display: flex; + flex-direction: row; + gap: var(--primer-stack-gap-condensed, 8px); + } + + & .Overlay-titleWrap { + display: flex; + padding: calc(var(--primer-stack-gap-condensed, 8px) * 0.75) 0 calc(var(--primer-stack-gap-condensed, 8px) * 0.75) var(--primer-stack-gap-condensed, 8px); + flex-direction: column; + flex-grow: 1; + gap: var(--primer-control-small-gap, 4px); + + & .Overlay-title { + margin: 0; + font-size: var(--primer-text-body-size-medium, 14px); + font-weight: var(--base-text-weight-semibold, 600); + } + + & .Overlay-description { + margin: 0; + font-size: var(--primer-text-body-size-small, 12px); + font-weight: var(--base-text-weight-normal, 400); + color: var(--color-fg-muted); + } + } + } +} + +/* generic body content slot */ +.Overlay-body { + padding: var(--primer-stack-padding-normal, 16px); + padding-top: 0; + overflow-y: auto; + scrollbar-width: thin; + font-size: var(--primer-text-body-size-medium, 14px); + flex-grow: 1; + + &.Overlay-body--paddingCondensed { + padding: var(--primer-stack-padding-condensed, 8px); + padding-top: 0; + } + + &.Overlay-body--paddingNone { + padding: 0; + } +} + +/* generic footer slot */ +.Overlay-footer { + z-index: 1; + display: flex; + padding: 0 var(--primer-stack-padding-normal, 16px) var(--primer-stack-padding-normal, 16px) var(--primer-stack-padding-normal, 16px); + flex-direction: row; + flex-shrink: 0; + flex-wrap: wrap; + + &.Overlay-footer--divided { + padding-top: var(--primer-stack-padding-normal, 16px); + box-shadow: inset 0 var(--primer-borderWidth-thin, 1px) var(--color-border-default); + } + + &.Overlay-footer--alignStart { + justify-content: flex-start; + gap: var(--primer-stack-gap-condensed, 8px); + } + + &.Overlay-footer--alignCenter { + justify-content: center; + gap: var(--primer-stack-gap-condensed, 8px); + } + + &.Overlay-footer--alignEnd { + justify-content: flex-end; + gap: var(--primer-stack-gap-condensed, 8px); + } +} + +/* TODO: replace with refactored IconButton */ +.Overlay-closeButton { + position: relative; + display: grid; + width: var(--base-size-32, 32px); + height: var(--base-size-32, 32px); + padding: 0; + color: var(--color-fg-muted); + cursor: pointer; + user-select: none; + background-color: transparent; + border: var(--primer-borderWidth-thin, 1px) solid transparent; + border-radius: var(--primer-borderRadius-medium, 6px); + transition: 0.2s cubic-bezier(0.3, 0, 0.5, 1); + transition-property: color, background-color, border-color; + place-content: center; + align-self: flex-start; + flex-shrink: 0; + + &:hover, + &:focus { + background-color: var(--color-btn-hover-bg); + border: var(--primer-borderWidth-thin, 1px) solid var(--color-btn-hover-bg); + } + + /* Override .close-button's `border: 0` that triggers a border-color transition on hover */ + &.close-button { + border: var(--primer-borderWidth-thin, 1px) solid transparent; + } +} + +@define-mixin Overlay-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + display: flex; + background-color: var(--color-neutral-muted); +} + +@define-mixin Overlay-backdrop--transparent { + position: absolute; + z-index: 999; + background-color: transparent; +} + +/* variants must be mixins so we can extend within a media query (@extend is not supported inside media queries) */ + +/* border-radius repeats within placement options to ensure the original radius is reset when two classes co-exist */ + +/* center */ +@define-mixin Overlay-backdrop--center { + @mixin Overlay-backdrop; + + align-items: center; + justify-content: center; +} + +/* anchor */ +@define-mixin Overlay-backdrop--anchor { + @mixin Overlay-backdrop--transparent; + + .Overlay { + width: auto; + } +} + +/* anchor side(s) */ +@define-mixin Overlay-backdrop--side $responsiveVariant { + @mixin Overlay-backdrop; + + /* default left */ + align-items: center; + justify-content: left; + + &.Overlay-backdrop--placement-left$(responsiveVariant) { + align-items: center; + justify-content: left; + + & > .Overlay$(responsiveVariant) { + height: 100vh; + max-height: unset; + border-radius: var(--primer-borderRadius-large, 12px); + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + @media screen and (prefers-reduced-motion: no-preference) { + animation: 250ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-slideInRight; + } + } + } + + &.Overlay-backdrop--placement-right$(responsiveVariant) { + align-items: center; + justify-content: right; + + & > .Overlay$(responsiveVariant) { + height: 100vh; + max-height: unset; + border-radius: var(--primer-borderRadius-large, 12px); + border-top-right-radius: 0; + border-bottom-right-radius: 0; + + @media screen and (prefers-reduced-motion: no-preference) { + animation: 250ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-slideInLeft; + } + } + } + + &.Overlay-backdrop--placement-bottom$(responsiveVariant) { + align-items: end; + justify-content: center; + + & > .Overlay$(responsiveVariant) { + width: 100vw; + height: auto; + max-height: calc(100vh - 2rem); + border-radius: var(--primer-borderRadius-large, 12px); + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + + @media screen and (prefers-reduced-motion: no-preference) { + animation: 250ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-slideUp; + } + } + } + + &.Overlay-backdrop--placement-top$(responsiveVariant) { + align-items: start; + justify-content: center; + + & > .Overlay$(responsiveVariant) { + border-radius: var(--primer-borderRadius-large, 12px); + border-top-left-radius: 0; + border-top-right-radius: 0; + + @media screen and (prefers-reduced-motion: no-preference) { + animation: 250ms cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running Overlay--motion-slideDown; + } + } + } +} + +/* full width */ +@define-mixin Overlay-backdrop--full { + @mixin Overlay-backdrop; + + .Overlay { + width: 100%; + max-width: 100vw; + height: 100%; + max-height: 100vh; + border-radius: unset !important; + flex-grow: 1; + } +} + +/* Overlay variant classnames */ +.Overlay-backdrop--center { + @mixin Overlay-backdrop--center; +} + +.Overlay-backdrop--anchor { + @mixin Overlay-backdrop--anchor; +} + +.Overlay-backdrop--side { + @mixin Overlay-backdrop--side; +} + +.Overlay-backdrop--full { + @mixin Overlay-backdrop--full; +} + +/* responsive variants */ + +/* up to 767px */ +@media (--primer-viewportRange-narrowLandscape) { + .Overlay-backdrop--center-whenNarrow { + @mixin Overlay-backdrop--center; + } + + .Overlay-backdrop--anchor-whenNarrow { + @mixin Overlay-backdrop--anchor; + } + + .Overlay-backdrop--side-whenNarrow { + @mixin Overlay-backdrop--side -whenNarrow; + } + + .Overlay-backdrop--full-whenNarrow { + @mixin Overlay-backdrop--full; + } +} + +@keyframes Overlay--motion-slideDown { + from { + transform: translateY(-100%); + } +} + +@keyframes Overlay--motion-slideUp { + from { + transform: translateY(100%); + } +} + +@keyframes Overlay--motion-slideInRight { + from { + transform: translateX(-100%); + } +} + +@keyframes Overlay--motion-slideInLeft { + from { + transform: translateX(100%); + } +} diff --git a/app/components/primer/primer.pcss b/app/components/primer/primer.pcss index fc7d49d438..5f9cf72d58 100644 --- a/app/components/primer/primer.pcss +++ b/app/components/primer/primer.pcss @@ -2,6 +2,7 @@ @import "./alpha/action_list.pcss"; @import "./alpha/auto_complete.pcss"; @import "./alpha/banner.pcss"; +@import "./alpha/dialog.pcss"; @import "./alpha/dropdown.pcss"; @import "./alpha/tab_nav.pcss"; @import "./alpha/button_marketing.pcss"; diff --git a/demo/app/assets/stylesheets/application.css b/demo/app/assets/stylesheets/application.css index 152ef0a2b0..a9b32dd8e6 100644 --- a/demo/app/assets/stylesheets/application.css +++ b/demo/app/assets/stylesheets/application.css @@ -6,7 +6,6 @@ *= require @primer/css/dist/base.css *= require @primer/css/dist/forms.css *= require @primer/css/dist/layout.css - *= require @primer/css/dist/overlay.css *= require @primer/css/dist/utilities.css *= require @primer/css/dist/markdown.css */ diff --git a/test/css/component_specific_selectors_test.rb b/test/css/component_specific_selectors_test.rb index 59809026b7..6edd9e00fb 100644 --- a/test/css/component_specific_selectors_test.rb +++ b/test/css/component_specific_selectors_test.rb @@ -34,6 +34,9 @@ class ComponentSpecificSelectorsTest < Minitest::Test Primer::Alpha::Banner => [ ".Banner .Banner-close" ], + Primer::Alpha::Dialog => [ + ".Overlay" + ], Primer::Alpha::TabNav => [ ".tabnav-tab.selected", ".tabnav-extra",