diff --git a/.changeset/slow-poets-mate.md b/.changeset/slow-poets-mate.md
new file mode 100644
index 0000000000..80b58387b9
--- /dev/null
+++ b/.changeset/slow-poets-mate.md
@@ -0,0 +1,5 @@
+---
+"@primer/css": minor
+---
+
+Styles for the new Dialog Component
diff --git a/docs/content/components/dialog.md b/docs/content/components/dialog.md
new file mode 100644
index 0000000000..61e4e749ed
--- /dev/null
+++ b/docs/content/components/dialog.md
@@ -0,0 +1,58 @@
+---
+title: Dialog
+path: components/dialog
+status: Alpha
+source: 'https://github.com/github/github/tree/master/app/assets/stylesheets/experiments/modal-dialog.scss'
+bundle: dialog
+---
+
+Please note that the `
` element with `id="fake-container"` is not included in the component.
+
+```html live
+
+
Open dialog
+
+
+
+
+ This is the body of the dialogThis is the body of the dialogThis is the body of the dialog This is the body of
+ the dialog This is the body of the dialog This is the body of the dialog This is the body of the dialog This is
+ the body of the dialog This is the body of the dialog
+
+
+
+
+
+```
+
+[aria attributes]: https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties
diff --git a/docs/src/stories/components/Dialog/Dialog.stories.jsx b/docs/src/stories/components/Dialog/Dialog.stories.jsx
new file mode 100644
index 0000000000..c43b6a8f0e
--- /dev/null
+++ b/docs/src/stories/components/Dialog/Dialog.stories.jsx
@@ -0,0 +1,388 @@
+import React from 'react'
+import clsx from 'clsx'
+import {OverlayTemplate} from '../../ui-patterns/Overlay/Overlay.stories'
+
+export default {
+ title: 'Components/Dialog',
+ parameters: {
+ layout: 'padded'
+ },
+
+ excludeStories: ['DialogTemplate'],
+ argTypes: {
+ title: {
+ name: 'title',
+ type: {name: 'string', required: false},
+ description: 'The heading element of the dialog',
+ defaultValue: '',
+ table: {
+ category: 'HTML'
+ }
+ },
+ description: {
+ name: 'description',
+ type: 'string',
+ description: 'The sub-heading element of the dialog',
+ defaultValue: '',
+ table: {
+ category: 'HTML'
+ }
+ },
+ toggleOverlay: {
+ control: {type: 'boolean'},
+ description: 'show/hide overlay',
+ defaultValue: false,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showCloseButton: {
+ control: {type: 'boolean'},
+ description: 'show/hide close button',
+ defaultValue: true,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showFooterButton: {
+ control: {type: 'boolean'},
+ description: 'show/hide footer button',
+ defaultValue: false,
+ table: {
+ category: 'Demo'
+ }
+ },
+ width: {
+ options: [0, 1, 2, 3, 4, 5], // iterator
+ mapping: [
+ 'Overlay--width-auto',
+ 'Overlay--width-small',
+ 'Overlay--width-medium',
+ 'Overlay--width-large',
+ 'Overlay--width-xlarge',
+ 'Overlay--width-xxlarge'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['auto', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
+ },
+ description: 'Width options: small: 256px, medium: 320px, large: 480px, xlarge: 640px, xxlarge: 960px',
+ table: {
+ category: 'CSS'
+ }
+ },
+ height: {
+ options: [0, 1, 2, 3, 4, 5], // iterator
+ mapping: [
+ 'Overlay--height-auto',
+ 'Overlay--height-xsmall',
+ 'Overlay--height-small',
+ 'Overlay--height-medium',
+ 'Overlay--height-large',
+ 'Overlay--height-xlarge'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['auto', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
+ },
+ description:
+ 'Height options: auto: adjusts to content, xsmall: 192px, small: 256px, medium: 320px, large: 432px, xlarge: 600px',
+ table: {
+ category: 'CSS'
+ }
+ },
+ headerVariant: {
+ options: [0, 1], // iterator
+ mapping: ['', 'Overlay-header--large'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['medium (default)', 'large']
+ },
+ description: 'medium (default), large header/description font-size + spacing',
+ table: {
+ category: 'CSS'
+ }
+ },
+ bodyPaddingVariant: {
+ options: [0, 1, 2], // iterator
+ mapping: ['', 'Overlay-body--paddingCondensed', 'Overlay-body--paddingNone'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['normal (default)', 'condensed', 'none']
+ },
+ description: 'body spacing',
+ table: {
+ category: 'CSS'
+ }
+ },
+ variantNarrow: {
+ options: [0, 1, 2, 3], // iterator
+ mapping: [
+ 'Overlay-backdrop--center-whenNarrow',
+ 'Overlay-backdrop--anchor-whenNarrow',
+ 'Overlay-backdrop--side-whenNarrow',
+ 'Overlay-backdrop--full-whenNarrow'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['center', 'anchored', 'side', 'full']
+ },
+ description: '',
+ table: {
+ category: 'Variant'
+ }
+ },
+ variantRegular: {
+ options: [0, 1, 2, 3], // iterator
+ mapping: [
+ 'Overlay-backdrop--center',
+ 'Overlay-backdrop--anchor',
+ 'Overlay-backdrop--side',
+ 'Overlay-backdrop--full'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['center', 'anchored', 'side', 'full']
+ },
+ description: '',
+ table: {
+ category: 'Variant'
+ }
+ },
+ placementNarrow: {
+ options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ mapping: [
+ 'Overlay-backdrop--placement-top-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-bottom-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-right-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-left-whenNarrow',
+ '',
+ '',
+ '',
+ ''
+ ],
+ control: {
+ type: 'inline-radio',
+ labels: [
+ 'top',
+ 'top-start',
+ 'top-center',
+ 'top-end',
+ 'bottom',
+ 'bottom-start',
+ 'bottom-center',
+ 'bottom-end',
+ 'right',
+ 'right-start',
+ 'right-center',
+ 'right-end',
+ 'left',
+ 'left-start',
+ 'left-center',
+ 'left-end',
+ 'reset'
+ ]
+ },
+ description: 'Positions overlay for narrow viewport range',
+ table: {
+ category: 'Placement'
+ }
+ },
+ placementRegular: {
+ options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ mapping: [
+ 'Overlay-backdrop--placement-top',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-bottom',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-right',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-left',
+ '',
+ '',
+ '',
+ ''
+ ],
+ control: {
+ type: 'inline-radio',
+ labels: [
+ 'top',
+ 'top-start',
+ 'top-center',
+ 'top-end',
+ 'bottom',
+ 'bottom-start',
+ 'bottom-center',
+ 'bottom-end',
+ 'right',
+ 'right-start',
+ 'right-center',
+ 'right-end',
+ 'left',
+ 'left-start',
+ 'left-center',
+ 'left-end',
+ 'reset'
+ ]
+ },
+ description: 'Positions overlay for narrow viewport range',
+ table: {
+ category: 'Placement'
+ }
+ },
+ headerRegion: {
+ control: {type: 'boolean'},
+ description:
+ 'A header region may be used to provide context to the user by displaying a title, description, and offering an easy-to-escape route with a Close button. Headers may also provide ways for the user to interact with the content, such as with search and tabs.',
+ defaultValue: true,
+ table: {
+ category: 'Demo'
+ }
+ },
+ footerRegion: {
+ control: {type: 'boolean'},
+ description:
+ 'The footer region may be used to show confirmation actions, navigation links, or other important elements that should appear outside of the content scrolling region.',
+ defaultValue: true,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showFooterDivider: {
+ control: {type: 'boolean'},
+ defaultValue: false,
+ description: 'Show dividers above footer',
+ table: {
+ category: 'CSS'
+ }
+ },
+ showHeaderDivider: {
+ control: {type: 'boolean'},
+ defaultValue: false,
+ description: 'Show dividers below header',
+ table: {
+ category: 'CSS'
+ }
+ },
+ headerContentSlot: {
+ description: 'Slot for additional header content',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ actionContentSlot: {
+ description: 'Slot for additional header action',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ motion: {
+ options: [0, 1], // iterator
+ mapping: [null, 'Overlay--motion-scaleFade'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['none', 'scaleFade']
+ },
+ description: 'Animation options for show/hide overlay',
+ table: {
+ category: 'CSS'
+ }
+ },
+ footerContentAlign: {
+ options: [0, 1, 2], // iterator
+ mapping: ['Overlay-footer--alignStart', 'Overlay-footer--alignCenter', 'Overlay-footer--alignEnd'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['start', 'center', 'end']
+ },
+ description: 'Align footer contents',
+ table: {
+ category: 'CSS'
+ }
+ },
+ role: {
+ description: 'Semantic role',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ ariaLabelledy: {
+ description: 'aria-labelledby',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ ariaDescribedby: {
+ description: 'aria-describedby',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ dataFocusTrap: {
+ description: 'data-focus-trap',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ titleId: {
+ description: 'title id',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ descriptionId: {
+ description: 'description id',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ }
+ }
+}
+
+export const Playground = OverlayTemplate.bind({})
+Playground.args = {
+ ...OverlayTemplate.args,
+ title: 'Dialog title',
+ description: 'Optional dialog description',
+ role: 'dialog',
+ width: 2,
+ height: 3,
+ ariaLabelledby: 'title-id',
+ ariaDescribedby: 'description-id',
+ dataFocusTrap: 'active',
+ footerContentAlign: 2,
+ showCloseButton: true,
+ headerVariant: 0,
+ bodyPaddingVariant: 0,
+ motion: 1,
+ descriptionId: 'description-id',
+ titleId: 'title-id',
+ showFooterDivider: false,
+ children:
Dialog body
,
+ headerRegion: true,
+ variantNarrow: 3,
+ variantRegular: 0
+}
diff --git a/docs/src/stories/components/Popover/Popover.stories.jsx b/docs/src/stories/components/Popover/Popover.stories.jsx
deleted file mode 100644
index 89119ea548..0000000000
--- a/docs/src/stories/components/Popover/Popover.stories.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import React from 'react'
-import clsx from 'clsx'
-// import { StoryTemplateName } from './OtherStoryFile.stories' // import stories for component compositions
-
-export default {
- title: 'Components/Popover',
- excludeStories: ['PopoverTemplateName'],
- layout: 'padded',
- argTypes: {
- size: {
- options: [0, 1], // iterator
- mapping: ['', 'Popover-message--large'], // values
- control: {
- type: 'select',
- labels: ['default', 'large']
- },
- table: {
- category: 'CSS'
- }
- },
- caretPosition: {
- options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // iterator
- mapping: [
- '',
- 'Popover-message--bottom',
- 'Popover-message--bottom-left',
- 'Popover-message--bottom-right',
- 'Popover-message--left',
- 'Popover-message--left-bottom',
- 'Popover-message--left-top',
- 'Popover-message--right',
- 'Popover-message--right-bottom',
- 'Popover-message--right-top',
- 'Popover-message--top-left',
- 'Popover-message--top-right',
- 'Popover-message--no-caret'
- ], // values
- control: {
- type: 'inline-radio',
- labels: [
- 'default (top)',
- 'bottom',
- 'bottom_left',
- 'bottom_right',
- 'left',
- 'left_bottom',
- 'left_top',
- 'right',
- 'right_bottom',
- 'right_top',
- 'top_left',
- 'top_right',
- 'none'
- ]
- },
- table: {
- category: 'CSS'
- }
- },
- messagePosition: {
- options: ['position-relative', 'position-absolute'],
- control: {
- type: 'inline-radio'
- },
- description: '',
- table: {
- category: 'CSS'
- }
- },
- headingText: {
- name: 'headingText',
- type: 'string',
- description: 'string',
- table: {
- category: 'HTML'
- }
- },
- primerUtilities: {
- name: 'headingText',
- type: 'string',
- description: 'Primer utility classes',
- table: {
- category: 'HTML'
- }
- },
- tag: {
- options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
- control: {type: 'inline-radio'},
- description: 'h4 default',
- table: {
- category: 'HTML'
- }
- }
- }
-}
-
-export const PopoverTemplateName = ({
- size,
- caretPosition,
- messagePosition,
- headingText,
- tag,
- children,
- trigger,
- triggerBottom,
- primerUtilities
-}) => (
- <>
- {trigger}
-
-
- {tag === 'h1' &&
{headingText} }
- {tag === 'h2' && {headingText} }
- {tag === 'h3' && {headingText} }
- {tag === 'h4' && {headingText} }
- {tag === 'h5' && {headingText} }
- {tag === 'h6' && {headingText} }
- {children}
-
-
- {triggerBottom}
- >
-)
-
-export const Playground = PopoverTemplateName.bind({})
-Playground.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: '',
- messagePosition: 'position-relative',
- primerUtilities: 'text-left p-4 mt-2 mx-aut'
-}
diff --git a/docs/src/stories/components/Popover/PopoverFeatures.stories.jsx b/docs/src/stories/components/Popover/PopoverFeatures.stories.jsx
deleted file mode 100644
index d908f14537..0000000000
--- a/docs/src/stories/components/Popover/PopoverFeatures.stories.jsx
+++ /dev/null
@@ -1,229 +0,0 @@
-import React from 'react'
-import clsx from 'clsx'
-import {PopoverTemplateName} from './Popover.stories.jsx'
-
-export default {
- title: 'Components/Popover/Features',
- parameters: {
- design: {
- type: 'figma',
- url: 'https://www.figma.com/file/GCvY3Qv8czRgZgvl1dG6lp/Primer-Web?node-id=410%3A3890'
- }
- }
-}
-
-export const Bottom = PopoverTemplateName.bind({})
-Bottom.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--bottom',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mx-auto mb-2 text-left'
-}
-Bottom.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const BottomRight = PopoverTemplateName.bind({})
-BottomRight.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--bottom-right',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mb-2 text-left'
-}
-BottomRight.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const BottomLeft = PopoverTemplateName.bind({})
-BottomLeft.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--bottom-left',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mb-2'
-}
-BottomLeft.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const Left = PopoverTemplateName.bind({})
-Left.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--left',
- messagePosition: 'position-relative',
- trigger: [
Trigger button ],
- primerUtilities: 'p-4 ml-2'
-}
-Left.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const LeftBottom = PopoverTemplateName.bind({})
-LeftBottom.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--left-bottom',
- messagePosition: 'position-relative',
- trigger: [
Trigger button ],
- primerUtilities: 'p-4 ml-2'
-}
-LeftBottom.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const LeftTop = PopoverTemplateName.bind({})
-LeftTop.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--left-top',
- messagePosition: 'position-relative',
- trigger: [
Trigger button ],
- primerUtilities: 'p-4 ml-2'
-}
-LeftTop.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const Right = PopoverTemplateName.bind({})
-Right.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--right',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mr-2'
-}
-Right.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const RightBottom = PopoverTemplateName.bind({})
-RightBottom.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--right-bottom',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mr-2'
-}
-RightBottom.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const RightTop = PopoverTemplateName.bind({})
-RightTop.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--right-top',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mr-2'
-}
-RightTop.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const TopLeft = PopoverTemplateName.bind({})
-TopLeft.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--top-left',
- messagePosition: 'position-relative',
- trigger: [
Trigger button ],
- primerUtilities: 'p-4 mt-2'
-}
-TopLeft.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const TopRight = PopoverTemplateName.bind({})
-TopRight.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: '',
- caretPosition: 'Popover-message--top-right',
- messagePosition: 'position-relative',
- trigger: [
Trigger button ],
- primerUtilities: 'text-left p-4 mt-2'
-}
-TopRight.decorators = [
- Story => (
-
-
-
- )
-]
-
-export const Large = PopoverTemplateName.bind({})
-Large.args = {
- tag: 'h4',
- headingText: 'Popover heading',
- size: 'Popover-message--large',
- caretPosition: 'Popover-message--bottom',
- messagePosition: 'position-relative',
- triggerBottom: [
Trigger button ],
- primerUtilities: 'p-4 mx-auto mb-2 text-left'
-}
-Large.decorators = [
- Story => (
-
-
-
- )
-]
diff --git a/docs/src/stories/ui-patterns/Overlay/Overlay.stories.jsx b/docs/src/stories/ui-patterns/Overlay/Overlay.stories.jsx
new file mode 100644
index 0000000000..b5a33ee15a
--- /dev/null
+++ b/docs/src/stories/ui-patterns/Overlay/Overlay.stories.jsx
@@ -0,0 +1,518 @@
+import clsx from 'clsx'
+import React from 'react'
+import ConditionalWrapper from '../../helpers/ConditionalWrapper'
+import {PatternFullBleed} from '../ActionList/ActionListFeatures.stories.jsx'
+const variant = {}
+export default {
+ title: 'UI Patterns/Overlay',
+ parameters: {
+ layout: 'padded'
+ },
+ excludeStories: ['OverlayTemplate'],
+ argTypes: {
+ title: {
+ name: 'title',
+ type: {name: 'string', required: false},
+ description: 'The heading element of the dialog',
+ defaultValue: '',
+ table: {
+ category: 'HTML'
+ }
+ },
+ description: {
+ name: 'description',
+ type: 'string',
+ description: 'The sub-heading element of the dialog',
+ defaultValue: '',
+ table: {
+ category: 'HTML'
+ }
+ },
+ toggleOverlay: {
+ control: {type: 'boolean'},
+ description: 'show/hide overlay',
+ defaultValue: false,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showCloseButton: {
+ control: {type: 'boolean'},
+ description: 'show/hide close button',
+ defaultValue: false,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showFooterButton: {
+ control: {type: 'boolean'},
+ description: 'show/hide footer button',
+ defaultValue: false,
+ table: {
+ category: 'Demo'
+ }
+ },
+ width: {
+ options: [0, 1, 2, 3, 4, 5], // iterator
+ mapping: [
+ 'Overlay--width-auto',
+ 'Overlay--width-small',
+ 'Overlay--width-medium',
+ 'Overlay--width-large',
+ 'Overlay--width-xlarge',
+ 'Overlay--width-xxlarge'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['auto', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
+ },
+ description: 'Width options: small: 256px, medium: 320px, large: 480px, xlarge: 640px, xxlarge: 960px',
+ table: {
+ category: 'CSS'
+ }
+ },
+ height: {
+ options: [0, 1, 2, 3, 4, 5], // iterator
+ mapping: [
+ 'Overlay--height-auto',
+ 'Overlay--height-xsmall',
+ 'Overlay--height-small',
+ 'Overlay--height-medium',
+ 'Overlay--height-large',
+ 'Overlay--height-xlarge'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['auto', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge']
+ },
+ description:
+ 'Height options: auto: adjusts to content, xsmall: 192px, small: 256px, medium: 320px, large: 432px, xlarge: 600px',
+ table: {
+ category: 'CSS'
+ }
+ },
+ headerVariant: {
+ options: [0, 1], // iterator
+ mapping: ['', 'Overlay-header--large'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['medium (default)', 'large']
+ },
+ description: 'medium (default), large header/description font-size + spacing',
+ table: {
+ category: 'CSS'
+ }
+ },
+ bodyPaddingVariant: {
+ options: [0, 1, 2], // iterator
+ mapping: ['', 'Overlay-body--paddingCondensed', 'Overlay-body--paddingNone'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['normal (default)', 'condensed', 'none']
+ },
+ description: 'body spacing',
+ table: {
+ category: 'CSS'
+ }
+ },
+ variantNarrow: {
+ options: [0, 1, 2, 3], // iterator
+ mapping: [
+ 'Overlay-backdrop--center-whenNarrow',
+ 'Overlay-backdrop--anchor-whenNarrow',
+ 'Overlay-backdrop--side-whenNarrow',
+ 'Overlay-backdrop--full-whenNarrow'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['center', 'anchored', 'side', 'full']
+ },
+ description: '',
+ table: {
+ category: 'Variant'
+ }
+ },
+ variantRegular: {
+ options: [0, 1, 2, 3], // iterator
+ mapping: [
+ 'Overlay-backdrop--center',
+ 'Overlay-backdrop--anchor',
+ 'Overlay-backdrop--side',
+ 'Overlay-backdrop--full'
+ ], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['center', 'anchored', 'side', 'full']
+ },
+ description: '',
+ table: {
+ category: 'Variant'
+ }
+ },
+ placementNarrow: {
+ options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ mapping: [
+ 'Overlay-backdrop--placement-top-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-bottom-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-right-whenNarrow',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-left-whenNarrow',
+ '',
+ '',
+ '',
+ ''
+ ],
+ control: {
+ type: 'inline-radio',
+ labels: [
+ 'top',
+ 'top-start',
+ 'top-center',
+ 'top-end',
+ 'bottom',
+ 'bottom-start',
+ 'bottom-center',
+ 'bottom-end',
+ 'right',
+ 'right-start',
+ 'right-center',
+ 'right-end',
+ 'left',
+ 'left-start',
+ 'left-center',
+ 'left-end',
+ 'reset'
+ ]
+ },
+ description: 'Positions overlay for narrow viewport range',
+ table: {
+ category: 'Placement'
+ }
+ },
+ placementRegular: {
+ options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+ mapping: [
+ 'Overlay-backdrop--placement-top',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-bottom',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-right',
+ '',
+ '',
+ '',
+ 'Overlay-backdrop--placement-left',
+ '',
+ '',
+ '',
+ ''
+ ],
+ control: {
+ type: 'inline-radio',
+ labels: [
+ 'top',
+ 'top-start',
+ 'top-center',
+ 'top-end',
+ 'bottom',
+ 'bottom-start',
+ 'bottom-center',
+ 'bottom-end',
+ 'right',
+ 'right-start',
+ 'right-center',
+ 'right-end',
+ 'left',
+ 'left-start',
+ 'left-center',
+ 'left-end',
+ 'reset'
+ ]
+ },
+ description: 'Positions overlay for narrow viewport range',
+ table: {
+ category: 'Placement'
+ }
+ },
+ headerRegion: {
+ control: {type: 'boolean'},
+ description:
+ 'A header region may be used to provide context to the user by displaying a title, description, and offering an easy-to-escape route with a Close button. Headers may also provide ways for the user to interact with the content, such as with search and tabs.',
+ defaultValue: true,
+ table: {
+ category: 'Demo'
+ }
+ },
+ footerRegion: {
+ control: {type: 'boolean'},
+ description:
+ 'The footer region may be used to show confirmation actions, navigation links, or other important elements that should appear outside of the content scrolling region.',
+ defaultValue: true,
+ table: {
+ category: 'Demo'
+ }
+ },
+ showFooterDivider: {
+ control: {type: 'boolean'},
+ defaultValue: false,
+ description: 'Show dividers above footer',
+ table: {
+ category: 'CSS'
+ }
+ },
+ showHeaderDivider: {
+ control: {type: 'boolean'},
+ defaultValue: false,
+ description: 'Show dividers below header',
+ table: {
+ category: 'CSS'
+ }
+ },
+ headerContentSlot: {
+ description: 'Slot for additional header content',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ actionContentSlot: {
+ description: 'Slot for additional header action',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ motion: {
+ options: [0, 1], // iterator
+ mapping: [null, 'Overlay--motion-scaleFade'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['none', 'scaleFade']
+ },
+ description: 'Animation options for show/hide overlay',
+ table: {
+ category: 'CSS'
+ }
+ },
+ footerContentAlign: {
+ options: [0, 1, 2], // iterator
+ mapping: ['Overlay-footer--alignStart', 'Overlay-footer--alignCenter', 'Overlay-footer--alignEnd'], // values
+ control: {
+ type: 'inline-radio',
+ labels: ['start', 'center', 'end']
+ },
+ description: 'Align footer contents',
+ table: {
+ category: 'CSS'
+ }
+ },
+ role: {
+ description: 'Semantic role',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ ariaLabelledy: {
+ description: 'aria-labelledby',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ ariaDescribedby: {
+ description: 'aria-describedby',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ dataFocusTrap: {
+ description: 'data-focus-trap',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ titleId: {
+ description: 'title id',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ },
+ descriptionId: {
+ description: 'description id',
+ control: {type: 'string'},
+ table: {
+ category: 'HTML'
+ }
+ }
+ }
+}
+
+const focusMethod = function getFocus() {
+ const dialog = document.getElementById('overlay-backdrop')[0]
+ dialog.focus()
+}
+
+const toggleDialog = () => {
+ const dialog = document.getElementById('overlay-backdrop')
+ dialog.classList.toggle('Overlay--hidden')
+ focusMethod()
+}
+
+export const OverlayTemplate = ({
+ title,
+ description,
+ toggleOverlay,
+ width,
+ height,
+ showFooterDivider,
+ showHeaderDivider,
+ headerRegion,
+ footerRegion,
+ headerContentSlot,
+ motion,
+ footerContentAlign,
+ showCloseButton,
+ showFooterButton,
+ actionContentSlot,
+ headerVariant,
+ bodyPaddingVariant,
+ role,
+ ariaLabelledby,
+ ariaDescribedby,
+ dataFocusTrap,
+ children,
+ titleId,
+ descriptionId,
+ variantNarrow,
+ variantRegular,
+ placementNarrow,
+ placementRegular
+}) => (
+ <>
+
+ Open overlay
+
+
+
+ {headerRegion && (
+
+ )}
+
{children}
+ {footerRegion && (
+
+ {showFooterButton && (
+
+ Continue
+
+ )}
+
+ )}
+
+
+ >
+)
+
+export const Playground = OverlayTemplate.bind({})
+Playground.storyName = 'Playground'
+Playground.args = {
+ title: 'This is the title of the dialog',
+ description: 'This is the subtitle of the dialog',
+ motion: 1,
+ footerContentAlign: 2,
+ showCloseButton: true,
+ showFooterButton: false,
+ headerContentSlot: '',
+ actionContentSlot: '',
+ headerVariant: 0,
+ bodyPaddingVariant: 0,
+ width: 1,
+ height: 3,
+ headerVariant: 0,
+ headerRegion: true,
+ footerRegion: true,
+ showFooterDivider: false,
+ showHeaderDivider: false,
+ role: '',
+ ariaDescribedby: '',
+ dataFocusTrap: ''
+}
diff --git a/src/core/index.scss b/src/core/index.scss
index 166da98b06..bc704ee041 100644
--- a/src/core/index.scss
+++ b/src/core/index.scss
@@ -24,6 +24,7 @@
@import '../pagination/index.scss';
@import '../tooltips/index.scss';
@import '../truncate/index.scss';
+@import '../overlay/index.scss';
// Utilities always go last so that they can override components
@import '../utilities/index.scss';
diff --git a/src/overlay/README.md b/src/overlay/README.md
new file mode 100644
index 0000000000..7d91b0f79a
--- /dev/null
+++ b/src/overlay/README.md
@@ -0,0 +1,24 @@
+---
+bundle: 'overlay'
+generated: true
+---
+
+# Primer CSS: `overlay` bundle
+
+## Usage
+
+Primer CSS source files are written in [SCSS]. To include this Primer CSS module in your own build, ensure that your `node_modules` directory is listed in your Sass include paths, then import it with:
+
+```scss
+@import '@primer/css/overlay/index.scss';
+```
+
+## Build
+
+The `@primer/css` npm package includes a standalone CSS build of this module in `dist/overlay.css`.
+
+## License
+
+[MIT](https://github.com/primer/css/blob/main/LICENSE) © [GitHub](https://github.com/)
+
+[scss]: https://sass-lang.com/documentation/syntax#scss
diff --git a/src/overlay/index.scss b/src/overlay/index.scss
new file mode 100644
index 0000000000..381d309c08
--- /dev/null
+++ b/src/overlay/index.scss
@@ -0,0 +1,2 @@
+@import '../support/index.scss';
+@import './overlay.scss';
diff --git a/src/overlay/overlay.scss b/src/overlay/overlay.scss
new file mode 100644
index 0000000000..88fdaedc93
--- /dev/null
+++ b/src/overlay/overlay.scss
@@ -0,0 +1,384 @@
+// stylelint-disable selector-max-compound-selectors, max-nesting-depth, selector-max-specificity, primer/borders
+// replace with primitive
+$primer-borderRadius-large: 0.75rem;
+
+.Overlay--hidden {
+ display: none !important;
+}
+
+.Overlay--visibilityHidden {
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+ opacity: 0;
+}
+
+.Overlay {
+ display: flex;
+ min-width: 192px;
+ flex-direction: column;
+ background-color: var(--color-canvas-overlay);
+ border-radius: $primer-borderRadius-large;
+ box-shadow: var(--color-overlay-shadow);
+ opacity: 1;
+
+ &.Overlay--height-auto {
+ height: auto;
+ }
+
+ &.Overlay--height-xsmall {
+ height: min(192px, 100% - 2rem);
+ }
+
+ &.Overlay--height-small {
+ height: min(256px, 100% - 2rem);
+ }
+
+ &.Overlay--height-medium {
+ height: min(320px, 100% - 2rem);
+ }
+
+ &.Overlay--height-large {
+ height: min(432px, 100% - 2rem);
+ }
+
+ &.Overlay--height-xlarge {
+ height: min(600px, 100% - 2rem);
+ }
+
+ &.Overlay--width-auto {
+ width: auto;
+ }
+
+ &.Overlay--width-small {
+ width: min(256px, 100% - 2rem);
+ }
+
+ &.Overlay--width-medium {
+ width: min(320px, 100% - 2rem);
+ }
+
+ &.Overlay--width-large {
+ // stylelint-disable-next-line primer/responsive-widths
+ width: min(480px, 100% - 2rem);
+ }
+
+ &.Overlay--width-xlarge {
+ // stylelint-disable-next-line primer/responsive-widths
+ width: min(640px, 100% - 2rem);
+ }
+
+ &.Overlay--width-xxlarge {
+ // stylelint-disable-next-line primer/responsive-widths
+ width: min(960px, 100% - 2rem);
+ }
+
+ &.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