+
+ North America (Widespread), Central America (Limited), South America (Limited)
+
+
+
+ Least Concern
+
+
+
+
+
+
+
+
+
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
+
+
+
+
\ 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;