diff --git a/cspell.json b/cspell.json index bd723e2f5..07c073bd7 100644 --- a/cspell.json +++ b/cspell.json @@ -45,6 +45,7 @@ "dogfood", "dropdowns", "easings", + "ecommerce", "endraw", "endregion", "enterkeyhint", diff --git a/docs/.eleventy.js b/docs/.eleventy.js index ad08c9420..3a449263c 100644 --- a/docs/.eleventy.js +++ b/docs/.eleventy.js @@ -22,12 +22,15 @@ const isDeveloping = process.argv.includes('--develop'); export default function (eleventyConfig) { // NOTE - alpha setting removes certain pages if (isAlpha) { - eleventyConfig.ignores.add('**/components/page.md'); eleventyConfig.ignores.add('**/experimental/**'); + eleventyConfig.ignores.add('**/layout/**'); + eleventyConfig.ignores.add('**/patterns/**'); + eleventyConfig.ignores.add('**/style-utilities/**'); } // Add template data eleventyConfig.addGlobalData('package', packageData); + eleventyConfig.addGlobalData('isAlpha', isAlpha); // Template filters - {{ content | filter }} eleventyConfig.addFilter('inlineMarkdown', content => markdown.renderInline(content || '')); diff --git a/docs/_includes/sidebar.njk b/docs/_includes/sidebar.njk index 013aa7637..b39f7b405 100644 --- a/docs/_includes/sidebar.njk +++ b/docs/_includes/sidebar.njk @@ -20,11 +20,12 @@ {# Components #} -

Components

+

+ Components + + +

{# Layout #} +{% if not isAlpha %}

Layout

+

+ Layout + + +

+{% endif %} {# Patterns #} +{% if not isAlpha %}

Patterns

+{% endif %} {# Theming #}

Theming

@@ -284,10 +294,12 @@ {# Style Utilities #} +{% if not isAlpha %}

Style Utilities

\ No newline at end of file + +{% endif %} diff --git a/docs/_layouts/blank.njk b/docs/_layouts/blank.njk new file mode 100644 index 000000000..6e75aec6e --- /dev/null +++ b/docs/_layouts/blank.njk @@ -0,0 +1,69 @@ + + + + + + + {% if noindex %}{% endif %} + + {{ title }} + + + + + {# Scripts #} + {# Hydration stuff #} + + + + + + {# Web Awesome #} + + + + + + + + {# Set the theme to prevent flashing #} + + + + + {% block beforeContent %}{% endblock %} + + {% block content %} + {{ content | safe }} + {% endblock %} + + {% block afterContent %}{% endblock %} + + + diff --git a/docs/_layouts/component.njk b/docs/_layouts/component.njk index f5253d477..7d1e57faf 100644 --- a/docs/_layouts/component.njk +++ b/docs/_layouts/component.njk @@ -17,6 +17,10 @@ > {{ component.status }} + {# TODO - add a pro flag for pro components #} + {% if component.tagName == 'wa-page' %} + PRO + {% endif %}

{{ component.summary | inlineMarkdown | safe }} @@ -62,7 +66,7 @@ {# Properties #} {% if component.properties.length %} -

Properties

+

Attributes & Properties

@@ -77,9 +81,9 @@ {% for prop in component.properties %}
- {{ prop.name }}
+ {{ prop.name }}
{% if prop.attribute %} -
{{ prop.attribute }}
+
{{ prop.attribute }}
{% endif %}
diff --git a/docs/assets/images/webawesome-logo.svg b/docs/assets/images/webawesome-logo.svg index ca05b787c..1d58e868e 100644 --- a/docs/assets/images/webawesome-logo.svg +++ b/docs/assets/images/webawesome-logo.svg @@ -1,3 +1,3 @@ - - - + + + \ No newline at end of file diff --git a/docs/assets/scripts/code-examples.js b/docs/assets/scripts/code-examples.js index 406dd96ee..cad380838 100644 --- a/docs/assets/scripts/code-examples.js +++ b/docs/assets/scripts/code-examples.js @@ -18,10 +18,12 @@ document.addEventListener('click', event => { const cdnUrl = document.documentElement.dataset.cdnUrl; const html = `\n` + - `\n\n` + - `\n\n` + + `\n` + + `\n` + + `\n` + + `\n\n` + `${code.textContent}`; - const css = 'body {\n font: 16px sans-serif;\n padding: 2rem;\n}'; + const css = 'html > body {\n font: 16px sans-serif;\n padding: 2rem;\n}'; const js = ''; const form = document.createElement('form'); diff --git a/docs/assets/styles/docs.css b/docs/assets/styles/docs.css index de1d02348..f1b0e8dac 100644 --- a/docs/assets/styles/docs.css +++ b/docs/assets/styles/docs.css @@ -88,9 +88,10 @@ wa-page > header { h2 { font-size: var(--wa-font-size-m); margin-block-end: var(--wa-space-m); - } - h2:not(:first-of-type) { - margin-block-start: var(--wa-space-xl); + + &:not(:first-of-type) { + margin-block-start: var(--wa-space-xl); + } } ul { border-inline-start: var(--wa-border-width-s) solid var(--wa-color-surface-border); @@ -113,6 +114,55 @@ wa-page > header { li a:hover { text-decoration: underline; } + + li wa-badge::part(base) { + font-size: var(--wa-font-size-2xs); + font-weight: var(--wa-font-weight-bold); + } + + li wa-icon { + color: var(--wa-color-text-quiet); + vertical-align: middle; + + &[name='flask'] { + margin-inline: 0.125em; + } + } +} + +/* Pro badges */ +wa-badge.pro::part(base) { + background-color: var(--wa-brand-orange); + border-color: var(--wa-brand-orange); +} + +#sidebar { + h2 { + color: var(--wa-color-text-quiet); + + a { + display: flex; + align-items: center; + gap: 0.4em; + color: var(--wa-color-text-normal); + text-decoration: none; + + wa-icon { + margin-block-end: -0.15em; + font-size: var(--wa-font-size-s); + font-weight: var(--wa-font-weight-action); + color: var(--wa-color-gray-70); + } + + &:hover { + color: var(--wa-color-brand-on-normal); + + wa-icon { + color: var(--wa-color-brand-on-quiet); + } + } + } + } } #sidebar-close-button { @@ -143,6 +193,14 @@ wa-page > main { margin-inline: auto; } +h1.title wa-badge { + vertical-align: middle; + + &::part(base) { + font-size: 1.5rem; + } +} + .component-info { margin-block-end: var(--wa-flow-spacing); } diff --git a/docs/docs/components/page-samples/documentation.md b/docs/docs/components/page-samples/documentation.md new file mode 100644 index 000000000..efb77fd91 --- /dev/null +++ b/docs/docs/components/page-samples/documentation.md @@ -0,0 +1,238 @@ +--- +title: Sample Docs Page +description: TODO +layout: blank +--- + + + + + + + + + +
+ +
+
+

Identification

+

Lorem ipsum odor amet, consectetuer adipiscing elit. Eget habitant scelerisque lectus ultrices nascetur aliquet sapien primis. Cursus sapien fusce semper nulla elit sociosqu lectus per sem. Sem ad porttitor dictum nisl pharetra tortor convallis. Sit molestie hendrerit porta dictum tortor posuere euismod magna. Mauris suspendisse pharetra finibus; eleifend etiam ridiculus.

+

Range and Habitat

+

Diam sed ipsum pretium porttitor class cubilia elementum. Blandit felis ligula habitant ultricies vulputate rutrum lacus commodo pulvinar. Nostra semper placerat lectus in dis eu. Sagittis ipsum placerat rhoncus lacus id eget. Erat pharetra aptent enim, augue accumsan ultricies inceptos habitasse. Senectus id maximus parturient tellus; fermentum posuere vulputate luctus. Ac tempus dapibus vehicula ligula ullamcorper sit duis.

+

Behavior

+

Erat vitae luctus arcu taciti malesuada pretium arcu justo primis. Cubilia vitae maecenas congue velit id netus arcu. Dictum vel pellentesque taciti fermentum risus consectetur amet. Faucibus commodo habitasse sem maximus praesent purus, dignissim tristique porta. Platea magna justo ipsum ut metus ac facilisi. Imperdiet laoreet pharetra maximus lacus tortor suscipit. Nam quisque iaculis orci porttitor pellentesque rhoncus. Molestie sagittis tincidunt quisque nisi non urna conubia.

+

Conservation

+

Nullam magna quam quisque eu varius integer. Inceptos donec facilisi risus himenaeos semper mollis habitasse. Vehicula lacus vivamus euismod pharetra mollis dictum. Ante ex tortor elementum eleifend habitasse orci aliquam. Fames erat senectus fames etiam dapibus cursus.

+
+
+
+ + +
+
+ + +
\ No newline at end of file diff --git a/docs/docs/components/page.md b/docs/docs/components/page.md index 578b91993..5b9b7f868 100644 --- a/docs/docs/components/page.md +++ b/docs/docs/components/page.md @@ -2,6 +2,7 @@ title: Page description: Layouts offer an easy way to scaffold pages using minimal markup. layout: component +isPro: true --- The layout component is designed to power full webpages. It is flexible enough to handle most modern designs and includes a simple mechanism for handling desktop and mobile navigation. diff --git a/docs/docs/experimental/default-page-spacing.md b/docs/docs/experimental/default-page-spacing.md new file mode 100644 index 000000000..d409a4817 --- /dev/null +++ b/docs/docs/experimental/default-page-spacing.md @@ -0,0 +1,100 @@ +--- +title: Default Layout and Spacing +description: TODO +layout: blank +--- + + + + +
+ Banner + Banner + Banner +
+
+ Header + Header + Header +
+ + + + +
+ Main Header + Main Header + Main Header +
+
+

Main

+

No flex properties here! The author can specify their own preferred content flow and layout in the default slot.

+
+
+ Main Footer + Main Footer + Main Footer +
+ +
+ Footer + Footer + Footer +
+
\ No newline at end of file diff --git a/docs/docs/patterns/ecommerce-order-summmary.md b/docs/docs/patterns/ecommerce-order-summary.md similarity index 100% rename from docs/docs/patterns/ecommerce-order-summmary.md rename to docs/docs/patterns/ecommerce-order-summary.md diff --git a/docs/docs/resources/changelog.md b/docs/docs/resources/changelog.md index c379f0665..17e8a098f 100644 --- a/docs/docs/resources/changelog.md +++ b/docs/docs/resources/changelog.md @@ -21,6 +21,8 @@ During the alpha period, things might break! We take breaking changes very serio - Added more resilient support for lazy loaded options in `` - Added support for vertical button groups - Added the `focus()` method to `` +- Fixed a bug in `` with scroll locking shifting viewports. +- Fixed a bug in `` when using `.show()` - Fixed a bug in `` when using `precision` - Fixed a bug in `` that allowed tabbing into the rating when readonly - Fixed a bug in `` where the title attribute would show with redundant info diff --git a/scripts/build.js b/scripts/build.js index f219447f9..53c86ac7a 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -106,10 +106,9 @@ async function generateStyles() { // NOTE - alpha setting omits all stylesheets except for these because we use them in the docs if (isAlpha) { await copy(join(rootDir, 'src/themes/applied.css'), join(cdnDir, 'themes/applied.css'), { overwrite: true }); - await copy(join(rootDir, 'src/themes/color_standard.css'), join(cdnDir, 'themes/color_standard.css'), { - overwrite: true - }); await copy(join(rootDir, 'src/themes/default.css'), join(cdnDir, 'themes/default.css'), { overwrite: true }); + await copy(join(rootDir, 'src/themes/layout.css'), join(cdnDir, 'themes/layout.css'), { overwrite: true }); + await copy(join(rootDir, 'src/themes/utilities.css'), join(cdnDir, 'themes/utilities.css'), { overwrite: true }); } else { await copy(join(rootDir, 'src/themes'), join(cdnDir, 'themes'), { overwrite: true }); } @@ -288,12 +287,18 @@ if (isDeveloping) { callbacks: { ready: (_err, instance) => { // 404 errors - instance.addMiddleware('*', (req, res) => { + instance.addMiddleware('*', async (req, res) => { if (req.url.toLowerCase().endsWith('.svg')) { // Make sure SVGs error out in dev instead of serve the 404 page res.writeHead(404); } else { - res.writeHead(302, { location: '/404.html' }); + try { + const notFoundTemplate = await readFile(join(siteDir, '404.html'), 'utf-8'); + res.writeHead(404); + res.write(notFoundTemplate || 'Page Not Found'); + } catch { + // We're probably disconnected for some reason, so fail gracefully + } } res.end(); diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 7b6584455..97e3d6bad 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -186,7 +186,7 @@ export default class WaDialog extends WebAwesomeElement { // Open or close the dialog if (this.open && !this.dialog.open) { this.show(); - } else if (this.dialog.open) { + } else if (!this.open && this.dialog.open) { this.open = true; this.requestClose(this.dialog); } diff --git a/src/components/page/page.styles.ts b/src/components/page/page.styles.ts index 9692513cc..ea28da538 100644 --- a/src/components/page/page.styles.ts +++ b/src/components/page/page.styles.ts @@ -3,6 +3,7 @@ import { css } from 'lit'; export default css` :host { display: block; + background-color: var(--wa-color-surface-default); box-sizing: border-box; height: 100%; --menu-width: auto; @@ -14,6 +15,82 @@ export default css` --scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px)); } + ::slotted( + :is( + [slot='banner'], + [slot='header'], + [slot='subheader'], + [slot='navigation-header'], + [slot='navigation'], + [slot='navigation-footer'], + [slot='menu'], + [slot='main-header'], + [slot='main-footer'], + [slot='aside'], + [slot='footer'] + ) + ) { + background-color: var(--wa-color-surface-default); + } + + ::slotted([slot='banner']) { + display: flex; + align-items: center; + justify-content: center; + gap: var(--wa-space-m); + padding: var(--wa-space-xs) var(--wa-space-m); + } + + ::slotted([slot='header']) { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--wa-space-m); + padding: var(--wa-space-m); + } + + ::slotted([slot='subheader']) { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--wa-space-m); + padding: var(--wa-space-xs) var(--wa-space-m); + } + + ::slotted([slot*='navigation']), + ::slotted([slot='menu']), + ::slotted([slot='aside']) { + display: flex; + flex-direction: column; + gap: var(--wa-space-m); + padding: var(--wa-space-m); + } + + ::slotted([slot='main-header']) { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--wa-space-m); + padding: var(--wa-space-m) var(--wa-space-3xl); + } + + ::slotted(:not([slot])) { + padding: var(--wa-space-3xl); + } + + ::slotted([slot='main-footer']), + ::slotted([slot='footer']) { + display: flex; + align-items: start; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--wa-space-m); + padding: var(--wa-space-3xl); + } + :host([disable-sticky~='banner']) :is([part~='header'], [part~='subheader']) { --banner-height: 0px !important; } diff --git a/src/internal/scroll.ts b/src/internal/scroll.ts index f26b17da2..7fb4c7e48 100644 --- a/src/internal/scroll.ts +++ b/src/internal/scroll.ts @@ -1,16 +1,24 @@ import { getOffset } from './offset.js'; -import { isServer } from 'lit'; const locks = new Set(); -const lockStyles = isServer ? null : document.createElement('style'); -if (lockStyles) { - lockStyles.textContent = ` - .wa-scroll-lock { - scrollbar-gutter: stable !important; - overflow: hidden !important; - } - `; +/** Returns the width of the document's scrollbar */ +function getScrollbarWidth() { + const documentWidth = document.documentElement.clientWidth; + return Math.abs(window.innerWidth - documentWidth); +} + +/** + * Used in conjunction with `scrollbarWidth` to set proper body padding in case the user has padding already on the `` element. + */ +function getExistingBodyPadding() { + const padding = Number(getComputedStyle(document.body).paddingRight.replace(/px/, '')); + + if (isNaN(padding) || !padding) { + return 0; + } + + return padding; } /** @@ -20,9 +28,27 @@ if (lockStyles) { export function lockBodyScrolling(lockingEl: HTMLElement) { locks.add(lockingEl); - if (lockStyles && !lockStyles.isConnected) { - document.body.append(lockStyles); + // When the first lock is created, set the scroll lock size to match the scrollbar's width to prevent content from + // shifting. We only do this on the first lock because the scrollbar width will measure zero after overflow is hidden. + if (!document.documentElement.classList.contains('wa-scroll-lock')) { + /** Scrollbar width + body padding calculation can go away once Safari has scrollbar-gutter support. */ + const scrollbarWidth = getScrollbarWidth() + getExistingBodyPadding(); // must be measured before the `wa-scroll-lock` class is applied + + let scrollbarGutterProperty = getComputedStyle(document.documentElement).scrollbarGutter; + + // default is auto, unsupported browsers is "undefined" + if (!scrollbarGutterProperty || scrollbarGutterProperty === 'auto') { + scrollbarGutterProperty = 'stable'; + } + + /** Sometimes the scrollbar width is 1px, even then, we assume nothing is overflowing. */ + if (scrollbarWidth < 2) { + // if there's no scrollbar, just set it to an empty string so whatever the user has set gets used. This is useful if the page is not overflowing and showing a scrollbar, or if the user has overflow: hidden, or any other reason a scrollbar may not be showing. + scrollbarGutterProperty = ''; + } + document.documentElement.style.setProperty('--wa-scroll-lock-gutter', scrollbarGutterProperty); document.documentElement.classList.add('wa-scroll-lock'); + document.documentElement.style.setProperty('--wa-scroll-lock-size', `${scrollbarWidth}px`); } } @@ -34,7 +60,7 @@ export function unlockBodyScrolling(lockingEl: HTMLElement) { if (locks.size === 0) { document.documentElement.classList.remove('wa-scroll-lock'); - lockStyles?.remove(); + document.documentElement.style.removeProperty('--wa-scroll-lock-size'); } } diff --git a/src/themes/applied.css b/src/themes/applied.css index 4e1896079..7cecc5293 100644 --- a/src/themes/applied.css +++ b/src/themes/applied.css @@ -9,6 +9,13 @@ html { margin: 0; } +:is(html, body):has(wa-page) { + min-height: 100%; + height: 100%; + padding: 0; + margin: 0; +} + *, *::before, *::after { diff --git a/src/themes/default.css b/src/themes/default.css index 9954966bf..0352e8651 100644 --- a/src/themes/default.css +++ b/src/themes/default.css @@ -229,7 +229,7 @@ --wa-line-height-normal: 1.6; --wa-line-height-expanded: 2; - --wa-link-decoration-default: underline color-mix(in oklab, var(--wa-color-text-link) 70%, transparent) dotted; + --wa-link-decoration-default: underline color-mix(in oklab, currentColor 70%, transparent) dotted; --wa-link-decoration-hover: underline; /** diff --git a/src/themes/utilities.css b/src/themes/utilities.css index f6f4cc88a..67098858f 100644 --- a/src/themes/utilities.css +++ b/src/themes/utilities.css @@ -1,3 +1,24 @@ +/* #region Scroll Lock */ +/* These scroll lock helpers are put into this CSS file to avoid strict CSPs that affect style tag loading. */ +@supports (scrollbar-gutter: stable) { + .wa-scroll-lock { + scrollbar-gutter: var(--wa-scroll-lock-gutter) !important; + } + + .wa-scroll-lock body { + overflow: hidden !important; + } +} + +/** This can go away once Safari has scrollbar-gutter support. */ +@supports not (scrollbar-gutter: stable) { + .wa-scroll-lock body { + padding-right: var(--wa-scroll-lock-size) !important; + overflow: hidden !important; + } +} +/* #endregion */ + /* #region Align Items */ .wa-align-items-start { align-items: flex-start;