+ `;
+ }
+
+ setTouchAction() {
+ this.touchAction = this.select.value;
+ }
+
+ handlePointer(ev: PointerEvent) {
+ const {type, x, y, buttons} = ev;
+ if (type === 'pointermove' && buttons === 0) {
+ return;
+ }
+ this.log = `${type} (${x}px, ${y}px)`;
+ }
+}
diff --git a/packages/lit-dev-content/samples/tutorials/pointer-events/04/before/project.json b/packages/lit-dev-content/samples/tutorials/pointer-events/04/before/project.json
new file mode 100644
index 000000000..0731b9883
--- /dev/null
+++ b/packages/lit-dev-content/samples/tutorials/pointer-events/04/before/project.json
@@ -0,0 +1,7 @@
+{
+ "extends": "/samples/base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ }
+}
diff --git a/packages/lit-dev-content/samples/tutorials/pointer-events/description.md b/packages/lit-dev-content/samples/tutorials/pointer-events/description.md
new file mode 100644
index 000000000..f029bbd17
--- /dev/null
+++ b/packages/lit-dev-content/samples/tutorials/pointer-events/description.md
@@ -0,0 +1 @@
+Learn how to handle PointerEvents with Lit.
diff --git a/packages/lit-dev-content/samples/tutorials/pointer-events/tutorial.json b/packages/lit-dev-content/samples/tutorials/pointer-events/tutorial.json
new file mode 100644
index 000000000..ba69eee7f
--- /dev/null
+++ b/packages/lit-dev-content/samples/tutorials/pointer-events/tutorial.json
@@ -0,0 +1,24 @@
+{
+ "header": "Pointer Events",
+ "difficulty": "Beginner",
+ "size": "small",
+ "duration": 10,
+ "category": "Learn",
+ "steps": [
+ {
+ "title": "Intro to Pointer Events"
+ },
+ {
+ "title": "Event Types"
+ },
+ {
+ "title": "Pointer Event Properties"
+ },
+ {
+ "title": "Pointer Capturing"
+ },
+ {
+ "title": "Controlling scrolling with touch-action"
+ }
+ ]
+}
diff --git a/packages/lit-dev-content/site/tutorials/content/pointer-events/00.md b/packages/lit-dev-content/site/tutorials/content/pointer-events/00.md
new file mode 100644
index 000000000..f8ee1e3bf
--- /dev/null
+++ b/packages/lit-dev-content/site/tutorials/content/pointer-events/00.md
@@ -0,0 +1,3 @@
+[Pointer Events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) are DOM events fired from any pointing device, including touch, mouse, and pen.
+
+This unified model for handling user input events allows you to handle mobile, desktop, and tablet interactions with one set of event listeners, and no need to handle "compatibility" mouse events for touch.
diff --git a/packages/lit-dev-content/site/tutorials/content/pointer-events/01.md b/packages/lit-dev-content/site/tutorials/content/pointer-events/01.md
new file mode 100644
index 000000000..55b50a9e8
--- /dev/null
+++ b/packages/lit-dev-content/site/tutorials/content/pointer-events/01.md
@@ -0,0 +1,23 @@
+Pointer Events are modelled after Mouse Events, and therefore have very similar naming.
+
+ * `pointerdown`
+ * Fired when a finger or pen touches the screen, or a mouse button is pressed.
+ * `pointerup`
+ * Fired when a finger or pen leaves the screen, or a mouse button is released.
+ * `pointermove`
+ * Fired when the mouse moves, or a finger or pen touching the screen moves.
+ * `pointerover`
+ * Fired when a pointer moves into an element.
+ * `pointerout`
+ * Fired when a pointer moves out of an element.
+ * `pointerenter`
+ * Similar to `pointerover`, but also fires for the element's descendants, and does not bubble.
+ * `pointerleave`
+ * Similar to `pointerout`, but also fires for the element's descendants, and does not bubble.
+
+However, since Pointer Events are also used for touch, there are several more events that relate to touch-specific scenarios.
+
+ * `pointercancel`
+ * This event is fired if the browser decides that a touch interaction should scroll instead. Once this is fired, no more pointer events will fire for this interaction.
+ * `gotpointercapture` and `lostpointercapture`
+ * These events relate to [Pointer Capturing](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#capturing_the_pointer), which changes where Pointer Events are targeted, and is covered in a later section.
diff --git a/packages/lit-dev-content/site/tutorials/content/pointer-events/02.md b/packages/lit-dev-content/site/tutorials/content/pointer-events/02.md
new file mode 100644
index 000000000..076a406cb
--- /dev/null
+++ b/packages/lit-dev-content/site/tutorials/content/pointer-events/02.md
@@ -0,0 +1,24 @@
+Pointer Events extend from Mouse Events, have access to all the same [properties](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent).
+
+In addition, Pointer Events have several new properties:
+
+ * `pointerType`
+ * This property will be one of `mouse`, `touch`, or `pen`, and allows you to determine the input source of the event.
+ * `isPrimary`
+ * This property will be `true` if the pointer firing events is considered the "primary" pointer for the interaction, and will generate `click` events and other UI interactions.
+ * For `pointerType === mouse`, this will always be `true`
+ * For `pen` or `touch` input, this will be true only for the first finger or pen to touch the screen.
+ * If you want to ignore multi-touch interactions, you can ignore pointers where `isPrimary === false`
+ * `pressure`
+ * This property is a number between `0` and `1`, determined by how much pressure was applied relative to the maximum pressure the device can measure
+ * When the device cannot determine pressure, this will read `0.5` when the pointer is down, and `0` otherwise.
+ * `height` and `width`
+ * These properties will give the CSS pixel size of the pointer.
+ * This will always be `1` for mouse.
+ * `tiltX` and `tiltY`
+ * These properties will report the angle of the pen to the screen when pressed in the X and Y dimensions from `-90` to `90`.
+ * These properties are `0` when the pen is perfectly perpendicular to the screen, and either `90` or `-90` when the pen is flat against the screen.
+ * `twist`
+ * This property reports the clockwise rotation of the pen around its axis in degrees.
+
+Use your mouse, touchscreen, or pen in this example to see how the properties represent the pointer.
diff --git a/packages/lit-dev-content/site/tutorials/content/pointer-events/03.md b/packages/lit-dev-content/site/tutorials/content/pointer-events/03.md
new file mode 100644
index 000000000..e3bf07cd6
--- /dev/null
+++ b/packages/lit-dev-content/site/tutorials/content/pointer-events/03.md
@@ -0,0 +1,15 @@
+Pointer Capturing allows for a pointer to be retargeted to a chosen element, rather than the normal one set via hit testing.
+
+In practice, this means you can make mouse interactions behave like touch, and touch interactions behave like mouse.
+For completeness, touch pointers are considered "implicitly captured".
+
+**Pointer Capturing API**
+
+* To capture a pointer, you can use the `setPointerCapture(pointerId)` method on any HTML element.
+* To release a pointer, you can use the `releasePointerCapture(pointerId)` method.
+
+**Pointer Capturing Events**
+
+When a pointer is captured or released, the element will fire a `gotpointercapture` or `lostpointercapture` event respectively.
+
+Use your mouse, touchscreen, or pen in this example to see how the capturing or releasing the pointer changes behavior when dragging outside the box.
diff --git a/packages/lit-dev-content/site/tutorials/content/pointer-events/04.md b/packages/lit-dev-content/site/tutorials/content/pointer-events/04.md
new file mode 100644
index 000000000..18b100fc9
--- /dev/null
+++ b/packages/lit-dev-content/site/tutorials/content/pointer-events/04.md
@@ -0,0 +1,20 @@
+Another powerful feature of Pointer Events is the ability to control the brower's native touch behavior with the use of the [`touch-action`](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) css property.
+
+A pointer will fire events until the browser determines that a native behavior should occur, such as scrolling or zooming, at which point the browser will fire a final `pointercancel` event for the interaction.
+
+**Touch Action Settings**
+
+* `auto`
+ * This is the default setting. This means that the browser will fire events until scrolling, zooming, or browser specific behavior such as "double tap to zoom" occurs.
+* `none`
+ * With this setting, the browser will always fire events, and will prevent all touch behaviors like scrolling and zooming.
+* `pan-left`, `pan-right`, `pan-x`, `pan-up`, `pan-down`, and `pan-y`
+ * These settings will let the browser scroll in the specified direction.
+ * `pan-x` is shorthand for `pan-left pan-right`
+ * `pan-y` is shorthand for `pan-up pan-down`
+* `pinch-zoom`
+ * This setting will let the browser perform pinch to zoom behaviors, but not scrolling or "double tap to zoom".
+* `manipulation`
+ * This setting is equivalent to `pan-x pan-y pinch-zoom`, only disabling "double tap to zoom" and other browser-specific behaviors.
+
+Try using a touchscreen with this example to see how the different settings for `touch-action` control event firing and browser behavior.
diff --git a/packages/lit-dev-content/site/tutorials/tutorials.11tydata.js b/packages/lit-dev-content/site/tutorials/tutorials.11tydata.js
index c5d9cf87d..fa770fe26 100644
--- a/packages/lit-dev-content/site/tutorials/tutorials.11tydata.js
+++ b/packages/lit-dev-content/site/tutorials/tutorials.11tydata.js
@@ -36,6 +36,7 @@ module.exports = async () => {
// Learn
loadTutorialData('intro-to-lit'),
loadTutorialData('advanced-templating'),
+ loadTutorialData('pointer-events'),
// Build
loadTutorialData('brick-viewer'),