From 92d87369fbe1a8eacd5dbb7c09c2ae3009424ff8 Mon Sep 17 00:00:00 2001 From: Jeremy Walton Date: Tue, 30 Dec 2025 15:01:05 -0500 Subject: [PATCH 1/3] include top border on navbar so it can be used in the footer without issue --- src/components/navbar.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/navbar.css b/src/components/navbar.css index db74452e..0f811f06 100644 --- a/src/components/navbar.css +++ b/src/components/navbar.css @@ -16,7 +16,9 @@ align-items: center; padding: var(--op-space-small) var(--_op-navbar-horizontal-spacing); background-color: var(--_op-navbar-background-color); - box-shadow: var(--op-border-bottom) var(--_op-navbar-border-color); + box-shadow: + var(--op-border-top) var(--_op-navbar-border-color), + var(--op-border-bottom) var(--_op-navbar-border-color); color: var(--_op-navbar-text-color); gap: var(--_op-navbar-content-spacing); From 191d49d4122a0d13a42599877855b55617e367dc Mon Sep 17 00:00:00 2001 From: Jeremy Walton Date: Tue, 30 Dec 2025 15:41:41 -0500 Subject: [PATCH 2/3] Re-implement modal for the dialog using modern approaches. Update documentation to reflect --- src/components/modal.css | 64 +++++------- src/core/tokens/base_tokens.css | 3 +- src/stories/Components/Modal/Modal.js | 42 +++++++- src/stories/Components/Modal/Modal.mdx | 97 ++++--------------- src/stories/Components/Modal/Modal.stories.js | 8 ++ 5 files changed, 93 insertions(+), 121 deletions(-) diff --git a/src/components/modal.css b/src/components/modal.css index 83a54fd5..fca5a869 100644 --- a/src/components/modal.css +++ b/src/components/modal.css @@ -41,7 +41,6 @@ } } -/* stylelint-disable no-descending-specificity */ .modal { /* Public API (customizable component options) */ --_op-modal-width: calc(141 * var(--op-size-unit)); /* 564px */ @@ -87,63 +86,46 @@ gap: var(--op-space-small); } } -/* stylelint-enable no-descending-specificity */ dialog.modal { - position: fixed; - display: block; padding: 0; border: none; - inset: 0; + overscroll-behavior: contain; + transform: scale(0.7); + transition: + display var(--op-transition-modal-time) allow-discrete, + overlay var(--op-transition-modal-time) allow-discrete, + transform var(--op-transition-modal-time), + opacity var(--op-transition-modal-time); &::backdrop { - /* The Dialog backdrop does not inheret from :root so these need to be duplicated here. */ - --op-color-black: hsl(0deg 0% 0%); - --_op-modal-backdrop-active-opacity: 0.5; - --op-opacity-none: 0; - --op-opacity-full: 1; - - animation: show-backdrop 300ms ease-in; - background: var(--op-color-black); - opacity: var(--_op-modal-backdrop-active-opacity); + overflow: hidden; + background-color: var(--op-color-black); + opacity: var(--op-opacity-none); + overscroll-behavior: contain; + transition: + display var(--op-transition-modal-time) allow-discrete, + overlay var(--op-transition-modal-time) allow-discrete, + transform var(--op-transition-modal-time), + opacity var(--op-transition-modal-time); } &[open] { opacity: var(--op-opacity-full); transform: scale(1); - } - - &.modal--closing { - opacity: var(--op-opacity-none); - transform: scale(0.7); &::backdrop { - animation: hide-backdrop 300ms ease-in; - opacity: var(--op-opacity-none); + opacity: var(--op-opacity-half); } } } -/* Using the Dialog element */ - -/* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog */ - -@keyframes show-backdrop { - from { - opacity: var(--op-opacity-none); - } - - to { - opacity: var(--_op-modal-backdrop-active-opacity); - } -} - -@keyframes hide-backdrop { - from { - opacity: var(--_op-modal-backdrop-active-opacity); - } +@starting-style { + dialog.modal[open] { + transform: scale(0.7); - to { - opacity: var(--op-opacity-none); + &::backdrop { + opacity: var(--op-opacity-none); + } } } diff --git a/src/core/tokens/base_tokens.css b/src/core/tokens/base_tokens.css index c8127f8c..e51f8145 100644 --- a/src/core/tokens/base_tokens.css +++ b/src/core/tokens/base_tokens.css @@ -203,7 +203,8 @@ --op-transition-accordion-content: height 300ms ease, content-visibility 300ms ease allow-discrete; --op-transition-input: all 120ms ease-in; --op-transition-sidebar: all 200ms ease-in-out; - --op-transition-modal: all 300ms ease-in; + --op-transition-modal-time: 300ms; + --op-transition-modal: all var(--op-transition-modal-time) ease-in; --op-transition-panel: right 400ms ease-in; --op-transition-tooltip: all 300ms ease-in 300ms; diff --git a/src/stories/Components/Modal/Modal.js b/src/stories/Components/Modal/Modal.js index 925ad39a..2ad765cf 100644 --- a/src/stories/Components/Modal/Modal.js +++ b/src/stories/Components/Modal/Modal.js @@ -1,4 +1,8 @@ -export const createModal = ({ header, body, footer = '', inlineDemo = false }) => { +export const createModal = ({ header, body, footer = '', inlineDemo = false, dialog = false }) => { + if (dialog) { + return createDialogModal({ header, body, footer }) + } + const element = document.createElement('div') element.classList = `modal-wrapper modal-wrapper--active ${inlineDemo ? 'modal-wrapper--demo' : ''}` @@ -31,3 +35,39 @@ export const createModal = ({ header, body, footer = '', inlineDemo = false }) = return element } + +export const createDialogModal = ({ header, body, footer = '' }) => { + const example = document.createElement('div') + + const footerContents = + footer !== '' + ? footer + : ` +
+ +
+ + ` + + example.innerHTML = ` + + + + + + + ` + + return example +} diff --git a/src/stories/Components/Modal/Modal.mdx b/src/stories/Components/Modal/Modal.mdx index e8e2a0a3..b66b7530 100644 --- a/src/stories/Components/Modal/Modal.mdx +++ b/src/stories/Components/Modal/Modal.mdx @@ -51,85 +51,28 @@ Modal can be used as a standalone component, however, it does have a few depende [\ The Dialog Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) -The modal styles will still work using the HTML dialog element. Simply add the `modal` class to the dialog element. No need for the wrapper classes or the active class. +The modal styles work well using the HTML dialog element. Simply add the `modal` class to the dialog element. No need for the wrapper classes or the active class. -```html - - - - - -``` - -You can then use Javascript to open the dialog: - -```javascript -const dialog = document.getElementById('my-dialog') -const showButton = document.getElementById('show-dialog') -const closeButton = document.getElementById('dialog-close') -const cancelButton = document.getElementById('dialog-cancel') - -// This handles the escape key as well as other events -// This is a bit of a gotcha to get the dialog to close and animate correctly -// Normally you would just call dialog.close() but this will not fade the backdrop correctly. -dialog.addEventListener('cancel', (event) => { - event.preventDefault() - - dialog.classList.add('modal--closing') // run animation here - - dialog.addEventListener( - 'animationend', - () => { - dialog.classList.remove('modal--closing') - dialog.close() // then run the default close method - }, - { once: true } - ) // add this to prevent bugs when reopening the modal -}) - -// This handles clicking outside the dialog -dialog.addEventListener('click', (event) => { - //This prevents issues with forms - if (event.target.tagName !== 'DIALOG') { - return - } + - const rect = event.target.getBoundingClientRect() +To open a `` modally by clicking a ` + +``` - console.log(clickedInDialog) +With [invoker commands](https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API), newly available in all browsers though only as of December 12, 2025, buttons can now perform actions on other elements declaratively, without the need for any JavaScript. - if (clickedInDialog === false) { - event.target.dispatchEvent(new Event('cancel')) - } -}) - -showButton.addEventListener('click', () => { - dialog.showModal() -}) +```html + + +``` -closeButton.addEventListener('click', () => { - dialog.dispatchEvent(new Event('cancel')) -}) +If you want to use the invoker approach in your project today and ensure backwards compatibility, you can use a polyfill. -cancelButton.addEventListener('click', () => { - dialog.dispatchEvent(new Event('cancel')) -}) +```js +import 'https://esm.sh/invokers-polyfill' ``` ## Modal API @@ -138,20 +81,18 @@ Styles are built on CSS variables scoped to the modal. Here are the variables that can be customized: -{/* prettier-ignore-start */} ```css +/* base tokens */ +--op-transition-modal-time +--op-transition-modal + /* modal-wrapper */ --_op-modal-backdrop-active-opacity /* modal */ --_op-modal-width --_op-modal-max-height - -/* dialog.modal::backdrop */ ---op-color-black ---_op-modal-backdrop-active-opacity ``` -{/* prettier-ignore-end */} ## Customizing Modal styles diff --git a/src/stories/Components/Modal/Modal.stories.js b/src/stories/Components/Modal/Modal.stories.js index 1abfc17e..8be8508b 100644 --- a/src/stories/Components/Modal/Modal.stories.js +++ b/src/stories/Components/Modal/Modal.stories.js @@ -34,3 +34,11 @@ export const Inline = { inlineDemo: true, }, } + +export const Dialog = { + args: { + header: 'Modal Title', + body: 'This is the contents of the modal!', + dialog: true, + }, +} From db3fcbf0ef167521963959cb5b00174b5fff10ed Mon Sep 17 00:00:00 2001 From: Jeremy Walton Date: Tue, 30 Dec 2025 15:49:29 -0500 Subject: [PATCH 3/3] Include left border on sidebar so it shows when used on the right --- src/components/sidebar.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/sidebar.css b/src/components/sidebar.css index 7ea36eda..dd6f3384 100644 --- a/src/components/sidebar.css +++ b/src/components/sidebar.css @@ -31,7 +31,9 @@ display: flex; flex-direction: column; background-color: var(--_op-sidebar-background-color); - box-shadow: var(--op-border-right) var(--_op-sidebar-border-color); + box-shadow: + var(--op-border-left) var(--_op-sidebar-border-color), + var(--op-border-right) var(--_op-sidebar-border-color); color: var(--_op-sidebar-text-color); inline-size: var(--__op-sidebar-width); min-inline-size: var(--__op-sidebar-width);