`;
+ }
+
+ static styles = css`
+ p {
+ color: blue;
+ }
+ div {
+ color: red;
+ }
+ `;
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/add-styles/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/add-styles/project.json
new file mode 100644
index 000000000..816404c3d
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/add-styles/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/input-or-textfield.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/input-or-textfield.ts
new file mode 100644
index 000000000..d85b50100
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/bind-tag-name/input-or-textfield.ts
@@ -0,0 +1,35 @@
+import { html, LitElement } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+import { html as staticHTML, StaticValue } from 'lit/static-html.js';
+import { live } from 'lit/directives/live.js';
+
+@customElement('input-or-textfield')
+export class MyElement extends LitElement {
+ // attribute is false because this is a value that can't be serialized to an
+ // HTML attribute
+ @property({ attribute: false }) tagLiteral: StaticValue|null = null;
+ @property() value = '';
+
+ render() {
+ return html`
+ ${
+ // NOTE: the live() directive prevents setting the .value property if
+ // the live value of the input / textfield already matches this.value.
+ // This is important since static html templates should not be thrashed
+ // due to performance concerns.
+ staticHTML`
+ <${this.tagLiteral}
+ @input=${this.#onInput}
+ .value=${live(this.value)}>${this.tagLiteral}>
+ `
+ }
+
`;
+ }
+
+ static styles = css`
+ .red {
+ color: red;
+ }
+ .blue {
+ color: blue;
+ }
+ `;
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/classes/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/classes/project.json
new file mode 100644
index 000000000..816404c3d
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/classes/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/my-element.ts
new file mode 100644
index 000000000..860091f41
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/my-element.ts
@@ -0,0 +1,26 @@
+import { html, LitElement } from 'lit';
+import { customElement, state } from 'lit/decorators.js';
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ @state() private someBoolean = false;
+
+ render() {
+ let someText = html`
Some text
`;
+
+ if (this.someBoolean) {
+ someText = html`
Some other text
`;
+ }
+
+ return html`
+
+
This is an inline ternary conditional
+ ${this.someBoolean ? html`
Some other text
` : html`
Some text
`}
+
This is a variable conditional
+ ${someText}
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/project.json
new file mode 100644
index 000000000..9c791c2a3
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/conditionals/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "200px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/my-element.ts
new file mode 100644
index 000000000..8bd747eee
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/my-element.ts
@@ -0,0 +1,20 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+import { trustedStyles, type CSSStyleSheet } from './trusted-stringified-css-source.js';
+
+// Use constructable stylesheets on TRUSTED CSS strings to use them in a LitElement
+const styles = new CSSStyleSheet();
+// this type assertion is needed for the older version of TS like that the lit.dev website uses
+(styles as unknown as CSSStyleSheet).replace(trustedStyles);
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ static styles = styles;
+ render() {
+ return html`
+
+ This should be red!
+
+ `;
+ }
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/project.json
new file mode 100644
index 000000000..e170474a0
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "trusted-stringified-css-source.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/trusted-stringified-css-source.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/trusted-stringified-css-source.ts
new file mode 100644
index 000000000..fce4ea3be
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/constructable-stylesheets/trusted-stringified-css-source.ts
@@ -0,0 +1,11 @@
+export const trustedStyles = `
+ div {
+ color: red;
+ }
+`;
+
+// This may be needed for some older versions of TS
+export type CSSStyleSheet = typeof globalThis['CSSStyleSheet'] & {
+ replaceSync(cssText: string): void;
+ replace(cssText: string): void;
+};
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/index.html
new file mode 100644
index 000000000..63ef6c4b2
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/my-element.ts
new file mode 100644
index 000000000..6fac25d2d
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/my-element.ts
@@ -0,0 +1,18 @@
+import { html, LitElement, css } from 'lit';
+import { customElement } from 'lit/decorators.js';
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ static styles = css`
+ p {
+ color: blue;
+ border: 1px solid black;
+ padding: 4px;
+ margin-block: 4px;
+ }
+ `;
+
+ render() {
+ return html`
This is in a shadow root!
`;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/project.json
new file mode 100644
index 000000000..875a71aaf
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/css-shadow-parts/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "120px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/index.html
new file mode 100644
index 000000000..d0fdb30d1
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/my-element.ts
new file mode 100644
index 000000000..40810f9c3
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/my-element.ts
@@ -0,0 +1,33 @@
+import { html, LitElement } from 'lit';
+import { customElement, property, state } from 'lit/decorators.js';import {ComplexAttributeConverter} from 'lit';
+
+/**
+ * Bidirectionally converts an array from an attribute to a property of the
+ * following format:
+ *
+ * array-attribute='1, "2", 3' to [1, '2', 3]
+ */
+export const arrayConverter: ComplexAttributeConverter> = {
+ toAttribute: (array: Array) => {
+ return JSON.stringify(array).substring(1, JSON.stringify(array).length - 1);
+ },
+ fromAttribute: (value: string) => {
+ try {
+ return JSON.parse(`[${value}]`);
+ } catch {
+ return [];
+ }
+ }
+};
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ @property({ converter: arrayConverter, reflect: true })
+ array: Array = [];
+
+ render() {
+ return this.array.map((item) =>
+ html`
${typeof item}: ${item}
`
+ );
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/project.json
new file mode 100644
index 000000000..9c791c2a3
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/custom-attribute-converter/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "200px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/game-player.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/game-player.ts
new file mode 100644
index 000000000..fdfdab6a8
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/game-player.ts
@@ -0,0 +1,18 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+
+export type ScoreEvent = CustomEvent;
+
+@customElement('game-player')
+export class GamePlayer extends LitElement {
+ render() {
+ return html`
+
+
+ `;
+ }
+
+ handleScore(points: number) {
+ this.dispatchEvent(new CustomEvent('score', { detail: points, bubbles: true }));
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/index.html
new file mode 100644
index 000000000..0e004d712
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/project.json
new file mode 100644
index 000000000..9ccb50497
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "score-board.ts": {},
+ "game-player.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "250px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/score-board.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/score-board.ts
new file mode 100644
index 000000000..6a8d3dd36
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/data-up/score-board.ts
@@ -0,0 +1,20 @@
+import { html, LitElement } from 'lit';
+import { customElement, state } from 'lit/decorators.js';
+import './game-player.js';
+import type {ScoreEvent} from './game-player.js';
+
+@customElement('score-board')
+export class ScoreBoard extends LitElement {
+ @state() playerOneScore = 0;
+ @state() playerTwoScore = 0;
+
+ render() {
+ return html`
+
${this.playerOneScore} - ${this.playerTwoScore}
+
Player 1
+ this.playerOneScore += e.detail}>
+
Player 2
+ this.playerTwoScore += e.detail}>
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/hello-world.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/hello-world.ts
new file mode 100644
index 000000000..b4f5ba4d5
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/hello-world.ts
@@ -0,0 +1,9 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+
+@customElement('hello-world')
+export class HelloWorld extends LitElement {
+ render() {
+ return html`
Hello, world!
`;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/index.html
new file mode 100644
index 000000000..c53974764
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/project.json
new file mode 100644
index 000000000..6430bb8e9
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/define/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "hello-world.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/my-element.ts
new file mode 100644
index 000000000..02705851a
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/derived-state/my-element.ts
@@ -0,0 +1,23 @@
+import { html, LitElement } from 'lit';
+import { customElement, state } from 'lit/decorators.js';
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ @state() private count = 0;
+
+ get doubleCount() {
+ return this.count * 2;
+ }
+
+ render() {
+ return html`
+
` : ''}
+ `;
+ }
+
+ connectedCallback(): void {
+ super.connectedCallback();
+
+ // Only want to do this in the browser since the server doesn't have the
+ // concept of events or document.
+ if (!isServer) {
+ document.addEventListener('click', this.onDocumentClick);
+ this.addEventListener('focusin', this.onFocusin);
+ this.addEventListener('focusout', this.onFocusout);
+ }
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+
+ if (!isServer) {
+ // clean up to prevent memory leaks
+ document.removeEventListener('click', this.onDocumentClick);
+ // Garbage should also take care of removing these, but it's good practice
+ this.removeEventListener('focusin', this.onFocusin);
+ this.removeEventListener('focusout', this.onFocusout);
+ }
+ }
+
+ // Should be an arrow function and not a class method to ensure `this` is
+ // bound correctly.
+ private onFocusin = () => {
+ this.focusedWithin = true;
+ };
+
+ private onFocusout = () => {
+ this.focusedWithin = false;
+ };
+
+ private onDocumentClick = (e: MouseEvent) => {
+ const path = e.composedPath();
+ this.clickedOutside = !path.includes(this);
+ };
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/host-global-listeners/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/host-global-listeners/project.json
new file mode 100644
index 000000000..875a71aaf
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/host-global-listeners/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "120px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/my-element.js b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/my-element.js
new file mode 100644
index 000000000..952c50945
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/my-element.js
@@ -0,0 +1,16 @@
+import { html, LitElement } from 'lit';
+import styles from './styles.css' with { type: 'css' };
+
+export class MyElement extends LitElement {
+ static styles = styles;
+
+ render() {
+ return html`
+
+ This should be red!
+
+ `;
+ }
+}
+
+customElements.define('my-element', MyElement);
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/project.json
new file mode 100644
index 000000000..f9ed532bd
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.js": {},
+ "styles.css": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/styles.css b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/styles.css
new file mode 100644
index 000000000..538fa56f4
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import-attributes/styles.css
@@ -0,0 +1,3 @@
+div {
+ color: red;
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/another-component.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/another-component.ts
new file mode 100644
index 000000000..375269012
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/another-component.ts
@@ -0,0 +1,9 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+
+@customElement('another-component')
+export class AnotherComponent extends LitElement {
+ render() {
+ return html`(I'm another component.)`;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/hello-world.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/hello-world.ts
new file mode 100644
index 000000000..de5f2dccf
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/hello-world.ts
@@ -0,0 +1,10 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+import './another-component.js'
+
+@customElement('hello-world')
+export class HelloWorld extends LitElement {
+ render() {
+ return html`Hello, world! `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/index.html
new file mode 100644
index 000000000..c53974764
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/project.json
new file mode 100644
index 000000000..58dfe19ca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/import/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "hello-world.ts": {},
+ "another-component.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "100px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/index.html
new file mode 100644
index 000000000..5b273eb5d
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/my-element.ts
new file mode 100644
index 000000000..f6ed8d2d6
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/my-element.ts
@@ -0,0 +1,17 @@
+import { html, LitElement, css } from 'lit';
+import { customElement } from 'lit/decorators.js';
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ static styles = css`
+ p {
+ color: var(--sys-color-on-background, blue);
+ border: 1px solid var(--sys-color-outline, black);
+ padding: var(--comp-my-element-padding, 4px);
+ margin-block: var(--comp-my-element-margin, 4px);
+ }
+ `;
+ render() {
+ return html`
This is in a shadow root!
`;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/project.json
new file mode 100644
index 000000000..875a71aaf
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/inheriting-custom-props/project.json
@@ -0,0 +1,8 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-element.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "120px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/id-card.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/id-card.ts
new file mode 100644
index 000000000..b7a427e95
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/id-card.ts
@@ -0,0 +1,20 @@
+import { html, LitElement } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+@customElement('id-card')
+export class IdCard extends LitElement {
+ @property() name = '';
+ @property({ type: Number }) age = 0;
+ @property({ type: Boolean }) programmer = false;
+
+ render() {
+ return html`
+
${this.name}
+
Age: ${this.age}
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/index.html
new file mode 100644
index 000000000..d5f0f24d5
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/my-wallet.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/my-wallet.ts
new file mode 100644
index 000000000..0159d6911
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/my-wallet.ts
@@ -0,0 +1,12 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+import './id-card.js';
+
+@customElement('my-wallet')
+export class MyWallet extends LitElement {
+ render() {
+ return html`
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/project.json
new file mode 100644
index 000000000..c8faab7cf
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/pass-data-down/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "my-wallet.ts": {},
+ "id-card.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "170px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/id-card.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/id-card.ts
new file mode 100644
index 000000000..0feccc9d3
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/id-card.ts
@@ -0,0 +1,30 @@
+import { html, LitElement } from 'lit';
+import { customElement, property } from 'lit/decorators.js';
+
+@customElement('id-card')
+export class IdCard extends LitElement {
+ // Default attribute converter is string
+ @property() name = '';
+ // Number attribute converter converts attribtues to numbers
+ @property({ type: Number }) age = 0;
+ // Boolean attribute converter converts attribtues to boolean using
+ // .hasAttribute(). NOTE: boolean-attribute="false" will result in `true`
+ @property({ type: Boolean }) programmer = false;
+ // You can also specify the attribute name
+ @property({ type: Boolean, attribute: 'is-cool' }) isCool = false;
+
+ render() {
+ return html`
+
${this.name}
+
Age: ${this.age}
+
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/index.html
new file mode 100644
index 000000000..d5f0f24d5
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/my-wallet.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/my-wallet.ts
new file mode 100644
index 000000000..b88d3de0e
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/my-wallet.ts
@@ -0,0 +1,19 @@
+import { html, LitElement } from 'lit';
+import { customElement } from 'lit/decorators.js';
+import './id-card.js';
+
+@customElement('my-wallet')
+export class MyWallet extends LitElement {
+ render() {
+ return html`
+
+
+
+
+
+ `;
+ }
+}
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/project.json b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/project.json
new file mode 100644
index 000000000..b768f2dcd
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-properties/project.json
@@ -0,0 +1,9 @@
+{
+ "extends": "/samples/v3-base.json",
+ "files": {
+ "id-card.ts": {},
+ "my-wallet.ts": {},
+ "index.html": {}
+ },
+ "previewHeight": "400px"
+}
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/index.html b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/index.html
new file mode 100644
index 000000000..57a0c9aca
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/index.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/my-element.ts b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/my-element.ts
new file mode 100644
index 000000000..d73ac0ef2
--- /dev/null
+++ b/packages/lit-dev-content/samples/articles/lit-cheat-sheet/reactive-state/my-element.ts
@@ -0,0 +1,66 @@
+import { html, LitElement } from 'lit';
+import { customElement, state } from 'lit/decorators.js';
+
+@customElement('my-element')
+export class MyElement extends LitElement {
+ // Duration affects render, so it should be reactive. Though we don't want it
+ // to be exposed to consumers of my-element because we only want to expose
+ // `start()`, `pause()`, `reset()`, so we use a private state.
+ @state() private _duration = 0;
+ // isPlaying affects render, so it should be reactive. Though we don't want it
+ // to be exposed to consumers of my-element, so we use a private state.
+ @state() private _isPlaying = false;
+ private lastTick = 0;
+
+ render() {
+ const min = Math.floor(this._duration / 60000);
+ const sec = pad(min, Math.floor(this._duration / 1000 % 60));
+ const hun = pad(true, Math.floor(this._duration % 1000 / 10));
+
+ return html`
+