diff --git a/.storybook/decorators/index.js b/.storybook/decorators/index.js
index adecd25321e..f87e67b230e 100644
--- a/.storybook/decorators/index.js
+++ b/.storybook/decorators/index.js
@@ -2,6 +2,7 @@ import { makeDecorator, useEffect } from "@storybook/preview-api";
import { html } from "lit";
export { withContextWrapper } from "./contextsWrapper.js";
+export { withDownStateDimensionCapture } from "./withDownStateDimensionCapture.js";
export { withTestingPreviewWrapper } from "./withTestingPreviewWrapper.js";
/**
diff --git a/.storybook/decorators/withDownStateDimensionCapture.js b/.storybook/decorators/withDownStateDimensionCapture.js
new file mode 100644
index 00000000000..616852d34ce
--- /dev/null
+++ b/.storybook/decorators/withDownStateDimensionCapture.js
@@ -0,0 +1,19 @@
+export const withDownStateDimensionCapture = (selector) => (Story, context) => {
+ const captureDownStateDimensions = () => {
+ const components = document.querySelectorAll(selector);
+ components.forEach((component) => {
+ const { width, height } = component.getBoundingClientRect();
+ component.style.setProperty('--spectrum-downstate-width', `${width}px`);
+ component.style.setProperty('--spectrum-downstate-height', `${height}px`);
+ });
+ };
+
+ document.addEventListener("DOMContentLoaded", () => {
+ // Wait to make sure the story is fully rendered (otherwise width/height can be wrong)
+ setTimeout(() => {
+ captureDownStateDimensions();
+ }, 100);
+ });
+
+ return Story(context);
+};
diff --git a/.storybook/foundations/down-state/button-down-state.stories.js b/.storybook/foundations/down-state/button-down-state.stories.js
new file mode 100644
index 00000000000..e938e302aa6
--- /dev/null
+++ b/.storybook/foundations/down-state/button-down-state.stories.js
@@ -0,0 +1,32 @@
+import { Template } from "../../../components/button/stories/template";
+
+export default {
+ title: "Foundations/Down state",
+ description:
+ "Buttons allow users to perform an action or to navigate to another page. They have multiple styles for various needs, and are ideal for calling attention to where a user needs to do something in order to move forward in a flow.",
+ component: "Button",
+ args: {
+ rootClass: "spectrum-Button",
+ },
+ parameters: {
+ actions: {
+ handles: ['click .spectrum-Button'],
+ },
+ status: {
+ type: process.env.MIGRATED_PACKAGES.includes("button")
+ ? "migrated"
+ : undefined,
+ },
+ },
+ tags: ['foundation'],
+};
+
+export const ButtonDownState = Template.bind({});
+ButtonDownState.args = {
+ label: "Edit",
+ variant: "accent",
+ customStyles: {
+ "--spectrum-downstate-width": "72px",
+ "--spectrum-downstate-height": "32px"
+ }
+};
diff --git a/.storybook/foundations/down-state/checkbox-down-state.stories.js b/.storybook/foundations/down-state/checkbox-down-state.stories.js
new file mode 100644
index 00000000000..889c4fe4ce4
--- /dev/null
+++ b/.storybook/foundations/down-state/checkbox-down-state.stories.js
@@ -0,0 +1,27 @@
+import { Template } from "../../../components/checkbox/stories/template";
+
+export default {
+ title: "Foundations/Down state",
+ description:
+ "Checkboxes allow users to select multiple items from a list of individual items, or mark one individual item as selected.",
+ component: "Checkbox",
+ args: {
+ rootClass: "spectrum-Checkbox",
+ },
+ parameters: {
+ actions: {
+ handles: ['click input[type="checkbox"]'],
+ },
+ status: {
+ type: process.env.MIGRATED_PACKAGES.includes("checkbox")
+ ? "migrated"
+ : undefined,
+ },
+ },
+ tags: ['foundation'],
+};
+
+export const CheckboxDownState = Template.bind({});
+CheckboxDownState.args = {
+ label: "Checkbox",
+};
diff --git a/.storybook/foundations/down-state/down-state.mdx b/.storybook/foundations/down-state/down-state.mdx
new file mode 100644
index 00000000000..f07e65d2f8d
--- /dev/null
+++ b/.storybook/foundations/down-state/down-state.mdx
@@ -0,0 +1,44 @@
+import { Meta, Story } from '@storybook/blocks';
+import * as Checkbox from './checkbox-down-state.stories.js';
+import * as Button from './button-down-state.stories.js';
+
+
+
+# Down state
+
+Down state is a Spectrum 2 feature that creates the illusion of components being pressed-in when active. This functionality is already included in Spectrum 2 components that require down state in this project. It is implemented with a CSS transform. The implementation depends on the size of the interactable element, as shown in the examples below.
+
+## Examples
+
+### Minimum perspective
+
+For elements that have a width of 24px or less, the minimum perspective token is used to apply the down state. One example of a component that uses this token is the checkbox:
+
+
+
+In this case, we use the minimum perspective token:
+
+```
+transform:
+ perspective(var(--spectrum-component-size-minimum-perspective-down))
+ translateZ(var(--spectrum-component-size-difference-down));
+```
+
+### Calculated perspective
+
+For elements that have a width of greater than 24px, we need to use the component's width and height to apply the down state. One example of a component that uses this logic is the button:
+
+
+
+In this case, we use a max formula to calculate the perspective based on component width and height (this helps us account for components that may be very wide):
+
+```
+transform:
+ perspective(max(
+ var(--spectrum-downstate-height),
+ var(--spectrum-downstate-width) * var(--spectrum-component-size-width-ratio-down)
+ ))
+ translateZ(var(--spectrum-component-size-difference-down));
+```
+
+*Note that in this case, users are required to develop an implementation to determine the width and height of the component. Assign these values to the `--spectrum-downstate-width` and `--spectrum-downstate-height` custom properties in a `style` attribute on the HTML element to expose them for use in the CSS.*
diff --git a/.storybook/main.js b/.storybook/main.js
index 8910d9da2e9..8a024ddafa8 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -12,6 +12,8 @@ module.exports = {
stories: [
"../components/*/stories/*.stories.js",
"./guides/*.mdx",
+ "./foundations/*/*.mdx",
+ "./foundations/*/*.stories.js",
"./deprecated/*/*.stories.js",
],
rootDir: "../",
diff --git a/.storybook/manager.js b/.storybook/manager.js
index ceacc56a50d..fb843f695a3 100644
--- a/.storybook/manager.js
+++ b/.storybook/manager.js
@@ -50,7 +50,7 @@ addons.setConfig({
sidebar: {
showRoots: false,
filters: {
- patterns: (item) => !item.id.includes('forced-colors'),
+ patterns: (item) => !(item.id.includes('forced-colors') || item.tags.includes('foundation')),
}
},
});
diff --git a/.storybook/preview.js b/.storybook/preview.js
index 5578d8c2b73..8a3b42114e9 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -3,11 +3,11 @@ import DocumentationTemplate from './DocumentationTemplate.mdx';
import { withActions } from "@storybook/addon-actions/decorator";
import {
- withContextWrapper,
- withLanguageWrapper,
- withReducedMotionWrapper,
- withTestingPreviewWrapper,
- withTextDirectionWrapper,
+ withContextWrapper,
+ withLanguageWrapper,
+ withReducedMotionWrapper,
+ withTestingPreviewWrapper,
+ withTextDirectionWrapper,
} from "./decorators/index.js";
// https://github.com/storybookjs/storybook-addon-console
@@ -191,7 +191,7 @@ export const parameters = {
options: {
storySort: {
method: "alphabetical",
- order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Components', ['Docs', 'Default', '*'], '*'],
+ order: ['Guides', ['Contributing', '*', 'Adobe Code of Conduct', 'Changelog'], 'Foundations', 'Components', ['Docs', 'Default', '*'], '*'],
includeNames: true,
},
},
diff --git a/components/button/index.css b/components/button/index.css
index 748097bbc91..fbacf6151e1 100644
--- a/components/button/index.css
+++ b/components/button/index.css
@@ -26,7 +26,12 @@ governing permissions and limitations under the License.
--spectrum-button-focus-indicator-color: var(--spectrum-focus-indicator-color);
--spectrum-button-intended-icon-size: var(--spectrum-workflow-icon-size-50);
+<<<<<<< HEAD
--mod-progress-circle-position: absolute;
+=======
+ /* stylelint-disable-next-line spectrum-tools/no-unknown-custom-properties */
+ --spectrum-downstate-perspective: max(var(--spectrum-downstate-height), var(--spectrum-downstate-width) * var(--spectrum-component-size-width-ratio-down));
+>>>>>>> 5a532e656 (feat(downstate): docs + implementation for example components (#2520))
}
.spectrum-Button--sizeS {
@@ -45,6 +50,10 @@ governing permissions and limitations under the License.
--spectrum-button-bottom-to-text: var(--spectrum-button-bottom-to-text-small);
--spectrum-button-top-to-icon: var(--spectrum-component-top-to-workflow-icon-75);
--spectrum-button-intended-icon-size: var(--spectrum-workflow-icon-size-75);
+
+ &.spectrum-Button--iconOnly {
+ --spectrum-downstate-perspective: var(--spectrum-component-size-minimum-perspective-down);
+ }
}
.spectrum-Button--sizeM {
@@ -131,6 +140,10 @@ governing permissions and limitations under the License.
box-shadow: none;
}
+ &:active {
+ transform: perspective(var(--spectrum-downstate-perspective)) translateZ(var(--spectrum-component-size-difference-down));
+ }
+
.spectrum-Icon {
/* Any block-size difference between the intended workflow icon size and actual icon used.
Helps support any existing use of smaller UI icons instead of intended Workflow icons. */
diff --git a/components/button/package.json b/components/button/package.json
index c6c614db673..5ebcefed04f 100644
--- a/components/button/package.json
+++ b/components/button/package.json
@@ -1,6 +1,6 @@
{
"name": "@spectrum-css/button",
- "version": "12.0.3-next.0",
+ "version": "14.0.0-next.2",
"description": "The Spectrum CSS button component",
"license": "Apache-2.0",
"author": "Adobe",
diff --git a/components/button/stories/button.stories.js b/components/button/stories/button.stories.js
index cfd3c877d4f..d2cbe6ffe3f 100644
--- a/components/button/stories/button.stories.js
+++ b/components/button/stories/button.stories.js
@@ -1,5 +1,7 @@
import { html } from "lit";
import { styleMap } from "lit/directives/style-map.js";
+import { when } from "lit/directives/when.js";
+import { withDownStateDimensionCapture } from "../../../.storybook/decorators";
import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
import { Template as Typography } from "@spectrum-css/typography/stories/template.js";
@@ -12,6 +14,7 @@ import { Template } from "./template";
export default {
title: "Components/Button",
component: "Button",
+ decorators: [withDownStateDimensionCapture('.spectrum-Button:not(:disabled)')],
argTypes: {
size: {
name: "Size",
diff --git a/components/checkbox/index.css b/components/checkbox/index.css
index 2cb3aac560f..d4fbd4b9655 100644
--- a/components/checkbox/index.css
+++ b/components/checkbox/index.css
@@ -152,6 +152,12 @@ governing permissions and limitations under the License.
}
}
+ &:not(.is-readOnly):active {
+ .spectrum-Checkbox-input:not(:disabled) + .spectrum-Checkbox-box {
+ transform: perspective(var(--spectrum-component-size-minimum-perspective-down)) translateZ(var(--spectrum-component-size-difference-down));
+ }
+ }
+
/* Selected Invalid */
&.is-invalid {
.spectrum-Checkbox-input:checked + .spectrum-Checkbox-box,
diff --git a/components/checkbox/package.json b/components/checkbox/package.json
index bcc41b3e541..a3a392e9578 100644
--- a/components/checkbox/package.json
+++ b/components/checkbox/package.json
@@ -1,6 +1,6 @@
{
"name": "@spectrum-css/checkbox",
- "version": "8.1.6-next.0",
+ "version": "14.0.0-next.2",
"description": "The Spectrum CSS checkbox component",
"license": "Apache-2.0",
"author": "Adobe",